{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "accounting-core-gl",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "Book and tax in parallel. In-service date, not purchase date.",
  "files": [
    {
      "content": "import * as React from \"react\";\nimport { useScrollEdges } from \"@/hooks/ledger/use-scroll-edges\";\nimport { cn } from \"@/lib/ledger/cn\";\nimport { formatDate } from \"@/lib/ledger/fiscal-period\";\nimport { FROZEN_LABEL } from \"@/lib/ledger/frozen-label\";\nimport type { MoneyValue } from \"@/lib/ledger/money.types\";\nimport { ExclamationTriangleIcon } from \"@/components/ledger/icons\";\nimport { Money } from \"@/components/ledger/money\";\nimport { Rule } from \"@/components/ledger/rule\";\n\nexport type Asset = {\n  id: string;\n  description: string;\n  cost: MoneyValue;\n  /** In-service date, NOT purchase date. Labeled explicitly. */\n  inServiceDate: Date;\n  lifeMonths: number;\n  method: \"straight-line\" | \"double-declining\" | \"MACRS\";\n  convention: \"half-year\" | \"mid-month\" | \"mid-quarter\" | \"full-month\";\n  salvage: MoneyValue;\n  book: { accumulated: MoneyValue; nbv: MoneyValue };\n  tax: { accumulated: MoneyValue; nbv: MoneyValue };\n  disposal?: { proceeds: MoneyValue; nbvAtDisposal: MoneyValue; gainLoss: MoneyValue };\n};\n\nexport interface DepreciationScheduleProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  assets: Asset[];\n  /**\n   * Caps the scroller so the header and the asset column can freeze. Without a\n   * height the page scrolls instead and there is nothing for the frozen\n   * header to stick to.\n   */\n  maxHeight?: number;\n  periodLabel: string;\n  totals: { cost: MoneyValue; bookNbv: MoneyValue; taxNbv: MoneyValue };\n}\n\n/**\n * Book and tax books in parallel, with the difference shown. NBV can never\n * breach salvage — asserted. Disposal shows the working:\n * Proceeds − NBV = Gain/(Loss).\n */\nexport const DepreciationSchedule = React.forwardRef<HTMLDivElement, DepreciationScheduleProps>(\n  ({ className, assets, periodLabel, totals, maxHeight = 520, ...props }, ref) => {\n    const scroller = useScrollEdges<HTMLDivElement>();\n    const breaches = assets.filter((a) => Number(a.book.nbv.amount) < Number(a.salvage.amount));\n\n    return (\n      <div className={cn(\"space-y-3\", className)} ref={ref} {...props}>\n        {breaches.length > 0 ? (\n          <p\n            className=\"flex items-center gap-2 rounded-sm bg-negative-subtle px-3 py-2 text-body text-negative\"\n            role=\"alert\"\n          >\n            <ExclamationTriangleIcon aria-hidden=\"true\" className=\"size-4 shrink-0\" />\n            {breaches.length} asset{breaches.length === 1 ? \" has\" : \"s have\"} depreciated below salvage\n            value. That cannot happen and indicates a schedule error.\n          </p>\n        ) : null}\n\n        <div className=\"rounded-xl border border-border bg-surface\">\n          <div className=\"px-6 pb-4 pt-6\">\n            <p className=\"text-title-sm font-medium\">Depreciation schedule</p>\n            <p className=\"text-caption text-text-secondary\">{periodLabel} · book and tax in parallel</p>\n          </div>\n          <div className=\"ledger-frozen scrollbar-thin\" ref={scroller} style={{ maxHeight }}>\n            <table className=\"w-full min-w-[1000px]\">\n              <caption className=\"sr-only\">\n                Depreciation schedule. {periodLabel}, book and tax in parallel.\n              </caption>\n              <thead>\n                <tr className=\"border-b border-border-subtle\">\n                  <th\n                    className={cn(\n                      FROZEN_LABEL,\n                      \"py-3 pl-6 pr-3 text-left text-label font-medium text-text-secondary\",\n                    )}\n                    rowSpan={2}\n                    scope=\"col\"\n                  >\n                    Asset\n                  </th>\n                  <th\n                    className=\"px-3 py-3 text-right text-label font-medium text-text-secondary\"\n                    rowSpan={2}\n                    scope=\"col\"\n                  >\n                    Cost\n                  </th>\n                  <th\n                    className=\"px-3 py-3 text-left text-label font-medium text-text-secondary\"\n                    rowSpan={2}\n                    scope=\"col\"\n                  >\n                    In service\n                    <span className=\"block text-caption font-normal text-text-tertiary\">\n                      not purchase date\n                    </span>\n                  </th>\n                  <th\n                    className=\"px-3 py-3 text-left text-label font-medium text-text-secondary\"\n                    rowSpan={2}\n                    scope=\"col\"\n                  >\n                    Life / method\n                  </th>\n                  <th\n                    className=\"border-l border-border-subtle px-3 py-2 text-center text-label font-medium text-text-secondary\"\n                    colSpan={2}\n                    scope=\"col\"\n                  >\n                    Book\n                  </th>\n                  <th\n                    className=\"border-l border-border-subtle px-3 py-2 text-center text-label font-medium text-text-secondary\"\n                    colSpan={2}\n                    scope=\"col\"\n                  >\n                    Tax\n                  </th>\n                  <th\n                    className=\"border-l border-border-subtle py-3 pl-3 pr-6 text-right text-label font-medium text-text-secondary\"\n                    rowSpan={2}\n                    scope=\"col\"\n                  >\n                    Book/tax diff\n                  </th>\n                </tr>\n                <tr className=\"border-b border-border-subtle\">\n                  <th\n                    className=\"border-l border-border-subtle px-3 py-2 text-right text-caption text-text-secondary\"\n                    scope=\"col\"\n                  >\n                    Accumulated\n                  </th>\n                  <th className=\"px-3 py-2 text-right text-caption text-text-secondary\" scope=\"col\">\n                    NBV\n                  </th>\n                  <th\n                    className=\"border-l border-border-subtle px-3 py-2 text-right text-caption text-text-secondary\"\n                    scope=\"col\"\n                  >\n                    Accumulated\n                  </th>\n                  <th className=\"px-3 py-2 text-right text-caption text-text-secondary\" scope=\"col\">\n                    NBV\n                  </th>\n                </tr>\n              </thead>\n              <tbody>\n                {assets.map((a) => {\n                  const diff = Number(a.book.nbv.amount) - Number(a.tax.nbv.amount);\n                  return (\n                    <React.Fragment key={a.id}>\n                      <tr className=\"border-b border-border-subtle\">\n                        <th\n                          className={cn(FROZEN_LABEL, \"py-3 pl-6 pr-3 text-left text-body font-normal\")}\n                          scope=\"row\"\n                        >\n                          {a.description}\n                        </th>\n                        <td className=\"px-3 py-3 text-right\">\n                          <Money accounting value={a.cost} />\n                        </td>\n                        <td className=\"numeric px-3 py-3 text-caption text-text-secondary\">\n                          {formatDate(a.inServiceDate)}\n                        </td>\n                        <td className=\"px-3 py-3 text-caption text-text-secondary\">\n                          <span className=\"numeric\">{a.lifeMonths}mo</span> · {a.method}\n                          <span className=\"block text-text-tertiary\">{a.convention}</span>\n                        </td>\n                        <td className=\"border-l border-border-subtle px-3 py-3 text-right\">\n                          <Money accounting value={a.book.accumulated} />\n                        </td>\n                        <td className=\"px-3 py-3 text-right\">\n                          <Money accounting value={a.book.nbv} />\n                        </td>\n                        <td className=\"border-l border-border-subtle px-3 py-3 text-right\">\n                          <Money accounting value={a.tax.accumulated} />\n                        </td>\n                        <td className=\"px-3 py-3 text-right\">\n                          <Money accounting value={a.tax.nbv} />\n                        </td>\n                        <td className=\"border-l border-border-subtle py-3 pl-3 pr-6 text-right\">\n                          <Money\n                            accounting\n                            tone=\"secondary\"\n                            value={{ amount: diff, currency: a.cost.currency }}\n                          />\n                        </td>\n                      </tr>\n                      {a.disposal ? (\n                        <tr className=\"border-b border-border-subtle bg-surface-sunken\">\n                          <td className=\"py-2 pl-12 pr-6 text-caption text-text-secondary\" colSpan={9}>\n                            Disposed · Proceeds{\" \"}\n                            <Money accounting size=\"xs\" tone=\"inherit\" value={a.disposal.proceeds} /> − NBV{\" \"}\n                            <Money accounting size=\"xs\" tone=\"inherit\" value={a.disposal.nbvAtDisposal} /> ={\" \"}\n                            <Money accounting size=\"xs\" tone=\"inherit\" value={a.disposal.gainLoss} />{\" \"}\n                            {Number(a.disposal.gainLoss.amount) >= 0 ? \"gain\" : \"loss\"}\n                          </td>\n                        </tr>\n                      ) : null}\n                    </React.Fragment>\n                  );\n                })}\n              </tbody>\n              <tfoot>\n                <tr>\n                  <td colSpan={4} />\n                  <td className=\"px-3\" colSpan={2}>\n                    <Rule variant=\"subtotal\" />\n                  </td>\n                  <td className=\"px-3\" colSpan={2}>\n                    <Rule variant=\"subtotal\" />\n                  </td>\n                  <td className=\"pr-6\" />\n                </tr>\n                <tr>\n                  <th\n                    className={cn(FROZEN_LABEL, \"py-3 pl-6 pr-3 text-left text-label font-medium\")}\n                    scope=\"row\"\n                  >\n                    Total\n                  </th>\n                  <td className=\"px-3 py-3 text-right\">\n                    <Money accounting size=\"md\" value={totals.cost} />\n                  </td>\n                  <td colSpan={2} />\n                  <td className=\"border-l border-border-subtle px-3\" />\n                  <td className=\"px-3 py-3 text-right\">\n                    <Money accounting size=\"md\" value={totals.bookNbv} />\n                  </td>\n                  <td className=\"border-l border-border-subtle px-3\" />\n                  <td className=\"px-3 py-3 text-right\">\n                    <Money accounting size=\"md\" value={totals.taxNbv} />\n                  </td>\n                  <td className=\"border-l border-border-subtle pr-6\" />\n                </tr>\n              </tfoot>\n            </table>\n          </div>\n        </div>\n      </div>\n    );\n  },\n);\nDepreciationSchedule.displayName = \"DepreciationSchedule\";\n",
      "path": "components/ledger/depreciation-schedule.tsx",
      "target": "components/ledger/depreciation-schedule.tsx",
      "type": "registry:component"
    }
  ],
  "name": "depreciation-schedule",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/cn",
    "@lopifi/fiscal-period",
    "@lopifi/frozen-label",
    "@lopifi/icons",
    "@lopifi/money",
    "@lopifi/money.types",
    "@lopifi/rule",
    "@lopifi/use-scroll-edges"
  ],
  "title": "DepreciationSchedule",
  "type": "registry:component"
}
