PageSourceSearch

https://aivin-site.vercel.app/ui_kits/platform/valuation/advanced.aef048cd08.js

js aivin-site.vercel.app collected 2026-10-03 06:15:39 UTC 19,141 bytes, 490 lines download raw bytes

1(function(){
2const USES = {
3  'Insurance scheduling': {
4    value: 'insurance',
5    users: 'The owner and the insurer',
6    assumptions: 'Retail replacement in the most common market the owner would repurchase in. Assumes unbroken temperature-controlled storage and that the schedule is updated on material change.'
7  },
8  'Estate and probate': {
9    value: 'market',
10    users: 'The executor, the estate’s counsel and the tax authority',
11    assumptions: 'Fair market value between a willing buyer and a willing seller, neither under compulsion, as of the date of death. Blockage considered where a single holding exceeds market depth.'
12  },
13  'Charitable donation': {
14    value: 'market',
15    users: 'The donor, the recipient institution and the tax authority',
16    assumptions: 'Fair market value in the most appropriate market for the property. A qualified appraisal is required above the statutory threshold and must be signed by a credentialed appraiser.'
17  },
18  'Sale advisory': {
19    value: 'auction',
20    users: 'The owner',
21    assumptions: 'Net proceeds after seller’s commission and expected timing. Not an opinion of insurable or replacement value.'
22  },
23  'Portfolio review': {
24    value: 'market',
25    users: 'The owner and their advisers',
26    assumptions: 'Fair market value for management purposes. Not prepared for tax, insurance or litigation use.'
27  },
28  'Divorce or division of assets': {
29    value: 'market',
30    users: 'Both parties and their counsel',
31    assumptions: 'Fair market value as of the stated valuation date. Both parties are intended users; neither is the client for the purpose of confidentiality.'
32  }
33};
34function ScopeOfWork({
35  property,
36  purpose,
37  setPurpose
38}) {
39  const Rec = window.AIVP_RECORD,
40    Eng = window.AIVP_ENGINE;
41  const rows = Eng.valueAll(property.id);
42  const t = Eng.totals(rows, 'market');
43  const u = USES[purpose] || USES['Portfolio review'];
44  const [inspected, setInspected] = React.useState('none');
45  const audits = Rec.audits(property.id).filter(a => a.closed);
46  const EXTRAORDINARY = [{
47    on: t.unresolved > 0,
48    text: t.unresolved + ' lots have no comparable sales and are carried at acquisition cost. They are excluded from every market total and are not an opinion of value.'
49  }, {
50    on: inspected === 'none',
51    text: 'No bottle has been physically inspected by the appraiser. Condition and provenance are as recorded by the house.'
52  }, {
53    on: !audits.length,
54    text: 'No physical count has been reconciled against this Record. Quantities are as recorded.'
55  }, {
56    on: !!audits.length,
57    text: 'Quantities were reconciled against a physical count closed ' + (audits[0] ? window.longDate(audits[0].closed) : '') + '.'
58  }, {
59    on: property.dutyPerBottle > 0,
60    text: 'The collection is held in bond. Values are stated in bond; the duty-paid equivalent is carried against each lot and is not included in the total.'
61  }, {
62    on: property.licensed,
63    text: 'The property holds an on-premise license. Disposal routes available to an unlicensed owner may not be available to it.'
64  }].filter(x => x.on);
65  return React.createElement("div", {
66    style: {
67      display: 'grid',
68      gap: 14
69    }
70  }, React.createElement(window.ViewHead, {
71    title: "Scope of work"
72  }, "Written before the numbers, not after. An appraisal is only defensible if the intended use, the intended users and the assumptions were fixed in advance — a valuation prepared for insurance cannot be handed to a tax authority, and Aivin will not let the same document claim to be both."), React.createElement(window.Card, null, React.createElement("div", {
73    style: {
74      display: 'grid',
75      gridTemplateColumns: 'repeat(auto-fit,minmax(220px,1fr))',
76      gap: 14
77    }
78  }, React.createElement(window.Field, {
79    label: "Intended use"
80  }, React.createElement(window.Select, {
81    value: purpose,
82    onChange: setPurpose,
83    options: Object.keys(USES).map(k => ({
84      value: k,
85      label: k
86    }))
87  })), React.createElement(window.Field, {
88    label: "Inspection performed"
89  }, React.createElement(window.Select, {
90    value: inspected,
91    onChange: setInspected,
92    options: [{
93      value: 'none',
94      label: 'None — records only'
95    }, {
96      value: 'sample',
97      label: 'Sample — top lots inspected'
98    }, {
99      value: 'full',
100      label: 'Full — every lot inspected'
101    }]
102  })))), React.createElement("div", {
103    style: {
104      display: 'grid',
105      gridTemplateColumns: 'repeat(auto-fit,minmax(300px,1fr))',
106      gap: 14
107    }
108  }, React.createElement(window.Card, null, React.createElement(window.Label, null, "What follows from that choice"), React.createElement("div", {
109    style: {
110      display: 'grid',
111      gap: 0,
112      marginTop: 10
113    }
114  }, [['Basis of value required', Eng.BASES[u.value].label], ['Intended users', u.users], ['Effective date', window.longDate(Rec.AS_OF)], ['Property appraised', property.name + ' — ' + rows.length + ' lots, ' + window.num(t.bottles) + ' bottles'], ['Currency of report', property.currency], ['Prepared by', 'Aivin — appraisal support']].map(([k, v]) => React.createElement("div", {
115    key: k,
116    style: {
117      padding: '9px 0',
118      borderTop: '1px solid var(--border-subtle)'
119    }
120  }, React.createElement("div", {
121    style: {
122      display: 'flex',
123      justifyContent: 'space-between',
124      gap: 12,
125      alignItems: 'baseline'
126    }
127  }, React.createElement("span", {
128    style: {
129      fontSize: 12.5,
130      color: 'var(--text-muted)'
131    }
132  }, k), React.createElement("span", {
133    style: {
134      fontSize: 13,
135      color: 'var(--white)',
136      textAlign: 'right',
137      maxWidth: '62%'
138    }
139  }, v))))), React.createElement("div", {
140    style: {
141      fontSize: 12.5,
142      color: 'var(--text-body)',
143      marginTop: 12,
144      lineHeight: 1.65,
145      paddingTop: 12,
146      borderTop: '1px solid var(--border-subtle)'
147    }
148  }, u.assumptions)), React.createElement(window.Card, null, React.createElement(window.Label, null, "Extraordinary assumptions and limiting conditions"), React.createElement("div", {
149    style: {
150      fontSize: 12,
151      color: 'var(--text-muted)',
152      marginTop: 8,
153      lineHeight: 1.6
154    }
155  }, "Generated from the state of the Record, not typed in. If the Record improves, these shorten."), React.createElement("ul", {
156    style: {
157      margin: '12px 0 0',
158      paddingLeft: 18,
159      fontSize: 13,
160      color: 'var(--text-body)',
161      lineHeight: 1.75
162    }
163  }, EXTRAORDINARY.map((x, i) => React.createElement("li", {
164    key: i,
165    style: {
166      marginBottom: 7
167    }
168  }, x.text))))), React.createElement(window.Card, {
169    style: {
170      background: 'var(--surface-header)'
171    }
172  }, React.createElement(window.Label, null, "Competency and independence"), React.createElement("div", {
173    style: {
174      fontSize: 13,
175      color: 'var(--text-body)',
176      marginTop: 8,
177      lineHeight: 1.7,
178      maxWidth: 780
179    }
180  }, "Aivin assembles the schedule, the evidence and the working. It does not hold itself out as a credentialed appraiser, has no interest in the property, and its fee does not depend on the value reported. The opinion of value is the appraiser's, and the report says so on its face until one signs it.")));
181}
182function ValueChange({
183  property,
184  onOpenLot
185}) {
186  const Rec = window.AIVP_RECORD,
187    Eng = window.AIVP_ENGINE;
188  const m = n => Eng.money(n, property);
189  const snaps = Rec.snapshots(property.id);
190  const signed = snaps.filter(s => s.signedBy);
191  const lastSigned = signed.length ? signed[signed.length - 1] : null;
192  const recert = React.useMemo(() => {
193    if (!lastSigned) return null;
194    const eff = new Date(lastSigned.effective + 'T00:00:00Z');
195    const due = new Date(eff);
196    due.setUTCFullYear(due.getUTCFullYear() + 1);
197    const asOf = new Date(Rec.AS_OF + 'T00:00:00Z');
198    const months = Math.round((due - asOf) / (1000 * 60 * 60 * 24 * 30.44));
199    return {
200      due: due.toISOString().slice(0, 10),
201      months,
202      overdue: months < 0
203    };
204  }, [lastSigned && lastSigned.id]);
205  const [snapId, setSnapId] = React.useState((lastSigned || snaps[snaps.length - 1] || {}).id || '');
206  const snap = snaps.find(s => s.id === snapId);
207  const bridge = snap ? Eng.changeSince(property.id, snap) : null;
208  if (!snaps.length) {
209    return React.createElement("div", {
210      style: {
211        display: 'grid',
212        gap: 14
213      }
214    }, React.createElement(window.ViewHead, {
215      title: "What moved"
216    }, "Opening value, everything that changed it, closing value."), React.createElement(window.Empty, {
217      title: "Nothing to compare against"
218    }, property.name, " has no snapshot yet, so there is no earlier position to bridge from. Take one in the Snapshots view — from then on Aivin can account for every pound and dollar of movement between valuations."));
219  }
220  const max = Math.max.apply(null, [bridge.opening, bridge.closing].concat(bridge.steps.map(s => Math.abs(s.amount)))) || 1;
221  return React.createElement("div", {
222    style: {
223      display: 'grid',
224      gap: 14
225    }
226  }, React.createElement(window.ViewHead, {
227    title: "What moved",
228    right: React.createElement(window.Field, {
229      label: "Compare against"
230    }, React.createElement(window.Select, {
231      value: snapId,
232      onChange: setSnapId,
233      options: snaps.slice().reverse().map(s => ({
234        value: s.id,
235        label: s.label + ' \u00b7 ' + s.effective
236      }))
237    }))
238  }, "The second question after \"what is it worth\" is always \"why is that different from last time\". Every pound between the two valuations is accounted for below, and nothing is left in a residual."), recert && React.createElement(window.Card, {
239    style: {
240      border: recert.overdue ? '1px solid rgba(240,179,126,.4)' : '1px solid var(--border-subtle)'
241    }
242  }, React.createElement(window.Label, {
243    style: recert.overdue ? {
244      color: 'var(--signal-caution,#F0B37E)'
245    } : undefined
246  }, recert.overdue ? 'Re-certification is overdue' : 'Re-certification'), React.createElement("div", {
247    style: {
248      fontSize: 12.5,
249      color: 'var(--text-body)',
250      marginTop: 8,
251      lineHeight: 1.65,
252      maxWidth: 780
253    }
254  }, "Last certified ", window.longDate(lastSigned.effective), " by ", lastSigned.signedBy, ", for ", lastSigned.purpose.toLowerCase(), ". Annual re-certification falls due ", window.longDate(recert.due), recert.overdue ? ' \u2014 ' + Math.abs(recert.months) + ' month' + (Math.abs(recert.months) === 1 ? '' : 's') + ' ago. The figures below are what the appraiser will be asked to account for.' : recert.months <= 0 ? ', inside the month. The figures below are what the appraiser will be asked to account for.' : ', ' + recert.months + ' month' + (recert.months === 1 ? '' : 's') + ' from the current effective date.', ' ', "The bridge is drawn from that signature rather than from the most recent snapshot, so every movement since the document was signed is accounted for in one place.", snapId !== lastSigned.id && React.createElement("span", null, " ", React.createElement("button", {
255    type: "button",
256    onClick: () => setSnapId(lastSigned.id),
257    style: {
258      background: 'none',
259      border: 0,
260      padding: 0,
261      font: 'inherit',
262      color: 'var(--aivin-blue)',
263      cursor: 'pointer'
264    }
265  }, "Compare against the last signed valuation instead.")))), React.createElement(window.Card, {
266    "data-tour": "change"
267  }, React.createElement("div", {
268    style: {
269      display: 'flex',
270      gap: 20,
271      flexWrap: 'wrap'
272    }
273  }, React.createElement(window.Stat, {
274    label: 'Opening \u00b7 ' + snap.effective,
275    value: m(bridge.opening)
276  }), React.createElement(window.Stat, {
277    label: "Net movement",
278    value: (bridge.net < 0 ? '\u2212' : '') + m(Math.abs(bridge.net)),
279    tone: bridge.net >= 0 ? 'mint' : undefined,
280    sub: bridge.opening ? window.pct(bridge.net / bridge.opening * 100) : undefined
281  }), React.createElement(window.Stat, {
282    label: 'Closing \u00b7 ' + window.longDate(Rec.AS_OF),
283    value: m(bridge.closing),
284    tone: "mint"
285  }))), React.createElement(window.Card, null, React.createElement(window.Label, null, "The bridge"), React.createElement("div", {
286    style: {
287      display: 'grid',
288      gap: 12,
289      marginTop: 14
290    }
291  }, [{
292    label: 'Opening position',
293    amount: bridge.opening,
294    base: true
295  }].concat(bridge.steps).concat([{
296    label: 'Closing position',
297    amount: bridge.closing,
298    base: true
299  }]).map((s, i) => React.createElement("div", {
300    key: i,
301    style: {
302      display: 'flex',
303      alignItems: 'center',
304      gap: 14
305    }
306  }, React.createElement("span", {
307    style: {
308      flex: '0 0 260px',
309      fontSize: 12.5,
310      color: s.base ? 'var(--white)' : 'var(--text-body)'
311    }
312  }, s.label), React.createElement("span", {
313    style: {
314      flex: 1,
315      height: 12,
316      position: 'relative',
317      background: 'rgba(255,255,255,.04)',
318      borderRadius: 3
319    }
320  }, React.createElement("span", {
321    style: {
322      position: 'absolute',
323      top: 0,
324      bottom: 0,
325      left: s.base || s.amount >= 0 ? 0 : undefined,
326      right: !s.base && s.amount < 0 ? 'auto' : undefined,
327      width: Math.abs(s.amount) / max * 100 + '%',
328      borderRadius: 3,
329      background: s.base ? 'rgba(255,255,255,.30)' : s.amount >= 0 ? 'var(--aivin-mint)' : 'var(--signal-avoid,#E08A8A)'
330    }
331  })), React.createElement("span", {
332    style: {
333      flex: '0 0 110px',
334      textAlign: 'right',
335      fontSize: 12.5,
336      fontVariantNumeric: 'tabular-nums',
337      color: s.base ? 'var(--white)' : s.amount >= 0 ? 'var(--aivin-mint)' : 'var(--signal-avoid,#E08A8A)'
338    }
339  }, s.base ? m(s.amount) : (s.amount < 0 ? '\u2212' : '+') + m(Math.abs(s.amount)))))), React.createElement("div", {
340    style: {
341      fontSize: 11.5,
342      color: 'var(--text-muted)',
343      marginTop: 14,
344      lineHeight: 1.6,
345      paddingTop: 12,
346      borderTop: '1px solid var(--border-subtle)'
347    }
348  }, "Depletion is valued at the per-bottle figure carried at the opening date, so a pour is never confused with a price move. Anything attributable to a condition, provenance, format or drink-window change is separated from market movement, because one is the cellar changing and the other is the world changing.")));
349}
350function Discounts({
351  property,
352  onOpenLot
353}) {
354  const Eng = window.AIVP_ENGINE;
355  const m = n => Eng.money(n, property);
356  const rows = Eng.valueAll(property.id);
357  const market = rows.reduce((n, v) => n + v.market, 0);
358  const blocks = Eng.blockage(property.id);
359  const blockTotal = blocks.reduce((n, b) => n + b.amount, 0);
360  const [fractional, setFractional] = React.useState(0);
361  const fracAmount = market * (fractional / 100);
362  return React.createElement("div", {
363    style: {
364      display: 'grid',
365      gap: 14
366    }
367  }, React.createElement(window.ViewHead, {
368    title: "Discounts"
369  }, "The adjustments a reviewing appraiser looks for first, and the two most commonly left out of a wine valuation. Both are off by default — Aivin applies neither without the appraiser deciding it applies."), React.createElement(window.Card, {
370    "data-tour": "discounts"
371  }, React.createElement("div", {
372    style: {
373      display: 'flex',
374      gap: 20,
375      flexWrap: 'wrap'
376    }
377  }, React.createElement(window.Stat, {
378    label: "Fair market, before discounts",
379    value: m(market)
380  }), React.createElement(window.Stat, {
381    label: "Blockage indicated",
382    value: m(blockTotal),
383    tone: blockTotal ? 'blue' : undefined,
384    sub: blocks.length + ' positions above market depth'
385  }), React.createElement(window.Stat, {
386    label: "Fractional interest",
387    value: m(fracAmount),
388    sub: fractional ? fractional + '% applied' : 'Not applied'
389  }), React.createElement(window.Stat, {
390    label: "After discounts",
391    value: m(market - blockTotal - fracAmount),
392    tone: "mint"
393  }))), React.createElement(window.Card, null, React.createElement(window.Label, null, "Blockage"), React.createElement("div", {
394    style: {
395      fontSize: 12.5,
396      color: 'var(--text-body)',
397      marginTop: 8,
398      lineHeight: 1.65,
399      maxWidth: 780
400    }
401  }, "A single owner holding forty cases of one wine cannot sell them all at the price of one case. Released together they clear at a discount; released slowly they carry for years. Appraisal practice calls the difference blockage, and ignoring it overstates a concentrated cellar."), React.createElement("div", {
402    style: {
403      marginTop: 12
404    }
405  }, blocks.length ? React.createElement(window.Table, {
406    cols: [{
407      id: 'w',
408      label: 'Position'
409    }, {
410      id: 'b',
411      label: 'Bottles',
412      right: true
413    }, {
414      id: 'm',
415      label: 'Fair market',
416      right: true
417    }, {
418      id: 'p',
419      label: 'Discount',
420      right: true
421    }, {
422      id: 'a',
423      label: 'Adjustment',
424      right: true
425    }, {
426      id: 'n',
427      label: 'Why'
428    }],
429    rows: blocks.map(b => ({
430      key: b.key,
431      cells: [b.key, b.bottles, m(b.market), Math.round(b.pct * 100) + '%', React.createElement("span", {
432        style: {
433          color: 'var(--signal-avoid,#E08A8A)'
434        }
435      }, '\u2212' + m(b.amount)), React.createElement("span", {
436        style: {
437          color: 'var(--text-muted)'
438        }
439      }, b.note)]
440    }))
441  }) : React.createElement(window.Empty, null, "No single position in this cellar exceeds two cases, so blockage does not arise."))), React.createElement(window.Card, null, React.createElement(window.Label, null, "Fractional interest"), React.createElement("div", {
442    style: {
443      fontSize: 12.5,
444      color: 'var(--text-body)',
445      marginTop: 8,
446      lineHeight: 1.65,
447      maxWidth: 780
448    }
449  }, "Where the subject is a share of a collection rather than the whole of it — a partnership stake, a divided estate, an undivided half — the share is worth less than its arithmetic proportion, because the holder cannot unilaterally sell, store or move the wine. Set the discount the facts support; Aivin will not choose it for you."), React.createElement("div", {
450    style: {
451      display: 'flex',
452      gap: 14,
453      alignItems: 'center',
454      flexWrap: 'wrap',
455      marginTop: 14
456    }
457  }, React.createElement("input", {
458    type: "range",
459    min: "0",
460    max: "40",
461    step: "1",
462    value: fractional,
463    onChange: e => setFractional(+e.target.value),
464    style: {
465      flex: 1,
466      minWidth: 220,
467      accentColor: 'var(--aivin-mint)'
468    }
469  }), React.createElement("span", {
470    style: {
471      fontSize: 14,
472      color: 'var(--white)',
473      minWidth: 150
474    }
475  }, fractional ? fractional + '% \u00b7 ' + m(fracAmount) : 'Not applied')), React.createElement("div", {
476    style: {
477      fontSize: 11.5,
478      color: 'var(--text-muted)',
479      marginTop: 10,
480      lineHeight: 1.6
481    }
482  }, "Any discount applied here appears on the face of the appraisal with the reason recorded against it. An unexplained discount is the fastest way to have a report rejected.")));
483}
484Object.assign(window, {
485  ScopeOfWork,
486  ValueChange,
487  Discounts,
488  USES
489});
490})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.