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.