{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "accounting-core-gl",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "Opening → movements → closing → tie. The universal shape.",
  "files": [
    {
      "content": "import * as React from \"react\";\nimport { cn } from \"@/lib/ledger/cn\";\nimport type { MoneyValue } from \"@/lib/ledger/money.types\";\nimport { CheckIcon, ExclamationTriangleIcon } from \"@/components/ledger/icons\";\nimport { Money } from \"@/components/ledger/money\";\nimport { Rule } from \"@/components/ledger/rule\";\n\nexport interface RollForwardProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  additions: MoneyValue;\n  adjustments: MoneyValue;\n  closing: MoneyValue;\n  explanation?: string;\n  /** Asserted against the GL. */\n  glBalance: MoneyValue;\n  opening: MoneyValue;\n  periodLabel: string;\n  /** Asserted against the prior period's closing. */\n  priorClosing?: MoneyValue;\n  releases: MoneyValue;\n  title: string;\n}\n\n/**\n * The most reusable component in the accounting family — every schedule is\n * this shape.\n *\n * Opening · + Additions · − Releases · ± Adjustments · = Closing, then\n * closing vs. GL. The difference is always visible, and non-zero is the\n * primary state of the screen with a required explanation.\n */\nexport const RollForward = React.forwardRef<HTMLDivElement, RollForwardProps>(\n  (\n    {\n      className,\n      title,\n      periodLabel,\n      opening,\n      priorClosing,\n      additions,\n      releases,\n      adjustments,\n      closing,\n      glBalance,\n      explanation,\n      ...props\n    },\n    ref,\n  ) => {\n    const currency = closing.currency;\n    const continuity = priorClosing == null || Number(priorClosing.amount) === Number(opening.amount);\n    const glDifference = Number(closing.amount) - Number(glBalance.amount);\n    const ties = glDifference === 0;\n    const ok = continuity && ties;\n\n    return (\n      <div\n        className={cn(\"rounded-xl border bg-surface\", ok ? \"border-border\" : \"border-negative\", className)}\n        ref={ref}\n        {...props}\n      >\n        <header className=\"border-b border-border-subtle px-6 py-4\">\n          <h3 className=\"text-title-sm font-medium\">{title}</h3>\n          <p className=\"text-caption text-text-secondary\">{periodLabel}</p>\n        </header>\n\n        <table className=\"w-full\">\n          <caption className=\"sr-only\">\n            {title} roll-forward for {periodLabel}\n          </caption>\n          <tbody>\n            <Line amount={opening} label=\"Opening balance\" />\n            <Line amount={additions} label=\"Additions\" sign=\"+\" />\n            <Line amount={releases} label=\"Releases\" sign=\"−\" />\n            <Line amount={adjustments} label=\"Adjustments\" sign=\"±\" />\n            <tr>\n              <td />\n              <td className=\"px-6\">\n                <Rule variant=\"subtotal\" />\n              </td>\n            </tr>\n            <Line amount={closing} emphasis label=\"Closing balance\" />\n            <tr>\n              <td />\n              <td className=\"px-6\">\n                <Rule variant=\"total\" />\n              </td>\n            </tr>\n            <Line amount={glBalance} label=\"General ledger balance\" muted />\n            <tr>\n              <th\n                className={cn(\n                  \"py-2 pl-6 pr-6 text-left text-label font-medium\",\n                  ties ? \"text-positive\" : \"text-negative\",\n                )}\n                scope=\"row\"\n              >\n                Difference\n              </th>\n              <td className={cn(\"px-6 py-2 text-right\", ties ? \"text-positive\" : \"text-negative\")}>\n                <Money accounting size=\"md\" tone=\"inherit\" value={{ amount: glDifference, currency }} />\n              </td>\n            </tr>\n          </tbody>\n        </table>\n\n        <div className=\"space-y-2 px-6 pb-6\">\n          {!continuity && priorClosing ? (\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              Opening does not equal the prior period’s closing of{\" \"}\n              <Money accounting tone=\"inherit\" value={priorClosing} />.\n            </p>\n          ) : null}\n\n          <p\n            className={cn(\n              \"flex items-center gap-2 rounded-sm px-3 py-2 text-body\",\n              ties ? \"bg-positive-subtle text-positive\" : \"bg-negative-subtle text-negative\",\n            )}\n            role={ties ? undefined : \"alert\"}\n          >\n            {ties ? (\n              <CheckIcon aria-hidden=\"true\" className=\"size-4 shrink-0\" />\n            ) : (\n              <ExclamationTriangleIcon aria-hidden=\"true\" className=\"size-4 shrink-0\" />\n            )}\n            {ties\n              ? \"Closing balance ties to the general ledger.\"\n              : \"Closing balance does not tie to the general ledger. This must be explained before the close can proceed.\"}\n          </p>\n\n          {!ties ? (\n            explanation ? (\n              <p className=\"rounded-sm bg-surface-sunken p-3 text-body\">{explanation}</p>\n            ) : (\n              <label className=\"block\">\n                <span className=\"mb-1 block text-label font-medium text-text-secondary\">\n                  Explanation (required)\n                </span>\n                <textarea\n                  className=\"w-full rounded-sm border border-border bg-surface p-2 text-body outline-none focus-visible:border-accent\"\n                  placeholder=\"What accounts for the difference?\"\n                  rows={2}\n                />\n              </label>\n            )\n          ) : null}\n        </div>\n      </div>\n    );\n  },\n);\nRollForward.displayName = \"RollForward\";\n\nfunction Line({\n  label,\n  amount,\n  sign,\n  emphasis,\n  muted,\n}: {\n  label: string;\n  amount: MoneyValue;\n  sign?: string;\n  emphasis?: boolean;\n  muted?: boolean;\n}) {\n  return (\n    <tr>\n      <th\n        className={cn(\n          \"py-2 pl-6 pr-6 text-left text-body font-normal\",\n          emphasis && \"font-medium\",\n          muted && \"text-text-secondary\",\n        )}\n        scope=\"row\"\n      >\n        {sign ? (\n          <span aria-hidden=\"true\" className=\"mr-2 inline-block w-3 text-text-tertiary\">\n            {sign}\n          </span>\n        ) : (\n          <span aria-hidden=\"true\" className=\"mr-2 inline-block w-3\" />\n        )}\n        {label}\n      </th>\n      <td className=\"px-6 py-2 text-right\">\n        <Money\n          accounting\n          size={emphasis ? \"md\" : \"sm\"}\n          tone={muted ? \"secondary\" : \"primary\"}\n          value={amount}\n        />\n      </td>\n    </tr>\n  );\n}\n",
      "path": "components/ledger/roll-forward.tsx",
      "target": "components/ledger/roll-forward.tsx",
      "type": "registry:component"
    }
  ],
  "name": "roll-forward",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/cn",
    "@lopifi/icons",
    "@lopifi/money",
    "@lopifi/money.types",
    "@lopifi/rule"
  ],
  "title": "RollForward",
  "type": "registry:component"
}
