@happyvertical/smrt-commerce 0.56.1 → 0.56.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -4
- package/agents/contract-money.md +6 -0
- package/agents/customer-vendor-ui.md +7 -1
- package/agents/document-money.md +4 -3
- package/agents/tax-summary.md +4 -2
- package/dist/chunks/{ContractLineItemCollection-DYjhj_pU.js → ContractLineItemCollection--BrGMtNz.js} +6 -6
- package/dist/chunks/ContractLineItemCollection--BrGMtNz.js.map +1 -0
- package/dist/chunks/{CustomerCollection-DQIujn4f.js → CustomerCollection-CjGaZSpf.js} +3 -3
- package/dist/chunks/{CustomerCollection-DQIujn4f.js.map → CustomerCollection-CjGaZSpf.js.map} +1 -1
- package/dist/chunks/{DocumentTaxCollection-BEpoqXAQ.js → DocumentTaxCollection-KOkaQT2M.js} +31 -11
- package/dist/chunks/DocumentTaxCollection-KOkaQT2M.js.map +1 -0
- package/dist/chunks/{InvoiceCollection-CKQflFjc.js → InvoiceCollection-BUe_8FT1.js} +14 -14
- package/dist/chunks/InvoiceCollection-BUe_8FT1.js.map +1 -0
- package/dist/chunks/{InvoiceLineItemCollection-CHm9zdqh.js → InvoiceLineItemCollection-CPQGghyd.js} +8 -7
- package/dist/chunks/InvoiceLineItemCollection-CPQGghyd.js.map +1 -0
- package/dist/chunks/{TaxCodeCollection-DOpzW4y8.js → TaxCodeCollection-DF2eaJed.js} +2 -2
- package/dist/chunks/{TaxCodeCollection-DOpzW4y8.js.map → TaxCodeCollection-DF2eaJed.js.map} +1 -1
- package/dist/chunks/{TaxComponentCollection-Cs2uXwEl.js → TaxComponentCollection-3x24xSNh.js} +3 -3
- package/dist/chunks/{TaxComponentCollection-Cs2uXwEl.js.map → TaxComponentCollection-3x24xSNh.js.map} +1 -1
- package/dist/chunks/{TaxSettingsCollection-BBTZDH1S.js → TaxSettingsCollection-DYYF-In-.js} +6 -6
- package/dist/chunks/{TaxSettingsCollection-BBTZDH1S.js.map → TaxSettingsCollection-DYYF-In-.js.map} +1 -1
- package/dist/chunks/{allocation-integrity-DI3G16BN.js → allocation-integrity-Cq0g6CfO.js} +3 -3
- package/dist/chunks/{allocation-integrity-DI3G16BN.js.map → allocation-integrity-Cq0g6CfO.js.map} +1 -1
- package/dist/chunks/{document-money-DNVhDJyC.js → document-money-oUDvdjOf.js} +10 -10
- package/dist/chunks/{document-money-DNVhDJyC.js.map → document-money-oUDvdjOf.js.map} +1 -1
- package/dist/chunks/{invoice-posting-BwV8g7z0.js → invoice-posting-CF3fE_Ec.js} +7 -7
- package/dist/chunks/{invoice-posting-BwV8g7z0.js.map → invoice-posting-CF3fE_Ec.js.map} +1 -1
- package/dist/chunks/{tenant-money-barrier-DjRXgMnX.js → tenant-money-barrier-y6EAa6vY.js} +2 -2
- package/dist/chunks/{tenant-money-barrier-DjRXgMnX.js.map → tenant-money-barrier-y6EAa6vY.js.map} +1 -1
- package/dist/chunks/{types-LDETvQ1C.js → types-Bbm0Xflc.js} +2 -1
- package/dist/chunks/types-Bbm0Xflc.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -23
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1057 -151
- package/dist/migrations/partyLinks.d.ts.map +1 -1
- package/dist/models/Contract.d.ts +10 -4
- package/dist/models/Contract.d.ts.map +1 -1
- package/dist/models/ContractLineItem.d.ts +13 -5
- package/dist/models/ContractLineItem.d.ts.map +1 -1
- package/dist/models/DocumentTax.d.ts.map +1 -1
- package/dist/models/Invoice.d.ts.map +1 -1
- package/dist/models/InvoiceLineItem.d.ts +12 -2
- package/dist/models/InvoiceLineItem.d.ts.map +1 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +2925 -5578
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +13 -13
- package/dist/chunks/ContractLineItemCollection-DYjhj_pU.js.map +0 -1
- package/dist/chunks/DocumentTaxCollection-BEpoqXAQ.js.map +0 -1
- package/dist/chunks/InvoiceCollection-CKQflFjc.js.map +0 -1
- package/dist/chunks/InvoiceLineItemCollection-CHm9zdqh.js.map +0 -1
- package/dist/chunks/types-LDETvQ1C.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InvoiceCollection-BUe_8FT1.js","names":["field","seq"],"sources":["../../src/models/Invoice.ts","../../src/collections/InvoiceCollection.ts"],"sourcesContent":["/**\n * Invoice model - billing document sent to customers\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { DatabaseInterface } from '@happyvertical/smrt-core/migrations';\nimport type { Journal, JournalEntryData } from '@happyvertical/smrt-ledgers';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n assertAllocationParent,\n claimAllocationParent,\n} from '../allocation-integrity.js';\nimport { minorToMajorUnits } from '../billing/units.js';\nimport {\n DOCUMENT_MONEY_READ_REBIND,\n DOCUMENT_MONEY_WRITE,\n type DocumentMoneyReadRebindCapability,\n type DocumentMoneyWriteCapability,\n} from '../document-money-internal.js';\nimport type { FrozenInvoiceAccountingOptions } from '../invoice-accounting-sync.js';\nimport { assertIntegerMinorUnits, MoneyValidationError } from '../money.js';\nimport {\n type AccountingInvoiceInput,\n type CancelRevenueOptions,\n type InvoiceCollectionMethod,\n type InvoiceKind,\n type InvoiceOptions,\n InvoiceStatus,\n type RecognizeRevenueOptions,\n type TaxRegistrationOptions,\n} from '../types/index.js';\nimport type { InvoiceLineItem } from './InvoiceLineItem.js';\n\n/*\n * There is deliberately no `INVOICE_EPSILON` here any more (#2401).\n *\n * Every amount on this model is integer minor units, so `subtotal + taxAmount`\n * is exact, the sum of allocations is exact, and every comparison below is a\n * plain integer comparison. The old `0.01` tolerance existed because the values\n * were floats; keeping it against integers would silently forgive a genuine\n * one-cent discrepancy, and the \"snap `totalAmount` to `subtotal + taxAmount`\n * afterwards\" workaround it required (to stay inside the tighter smrt-ledgers\n * `BALANCE_EPSILON`) is gone with it — exact arithmetic needs no snap.\n */\n\n/**\n * Legal status transitions for an Invoice. Keyed by the prior (persisted)\n * status; the value is the set of statuses it may move to. A status mapping\n * to itself (no-op re-save) is always permitted and handled separately.\n *\n * Forward path: DRAFT → SENT → VIEWED → PARTIAL → PAID, with OVERDUE reachable\n * from any open status and CANCELLED / WRITTEN_OFF as terminal exits. Payment\n * progress (DRAFT/SENT/VIEWED ↔ PARTIAL ↔ PAID) is driven by `updatePaymentStatus`,\n * which can move both forward and backward as allocations change, so those\n * edges are bidirectional here.\n */\nconst INVOICE_STATUS_TRANSITIONS: Record<InvoiceStatus, InvoiceStatus[]> = {\n [InvoiceStatus.DRAFT]: [\n InvoiceStatus.SENT,\n InvoiceStatus.PARTIAL,\n InvoiceStatus.PAID,\n InvoiceStatus.CANCELLED,\n InvoiceStatus.WRITTEN_OFF,\n ],\n [InvoiceStatus.SENT]: [\n InvoiceStatus.VIEWED,\n InvoiceStatus.PARTIAL,\n InvoiceStatus.PAID,\n InvoiceStatus.OVERDUE,\n InvoiceStatus.CANCELLED,\n InvoiceStatus.WRITTEN_OFF,\n ],\n [InvoiceStatus.VIEWED]: [\n InvoiceStatus.SENT,\n InvoiceStatus.PARTIAL,\n InvoiceStatus.PAID,\n InvoiceStatus.OVERDUE,\n InvoiceStatus.CANCELLED,\n InvoiceStatus.WRITTEN_OFF,\n ],\n [InvoiceStatus.PARTIAL]: [\n InvoiceStatus.DRAFT,\n InvoiceStatus.SENT,\n InvoiceStatus.VIEWED,\n InvoiceStatus.PAID,\n InvoiceStatus.OVERDUE,\n InvoiceStatus.CANCELLED,\n InvoiceStatus.WRITTEN_OFF,\n ],\n [InvoiceStatus.OVERDUE]: [\n InvoiceStatus.SENT,\n InvoiceStatus.VIEWED,\n InvoiceStatus.PARTIAL,\n InvoiceStatus.PAID,\n InvoiceStatus.CANCELLED,\n InvoiceStatus.WRITTEN_OFF,\n ],\n // PAID can fall back to PARTIAL / VIEWED / SENT when a payment allocation is\n // removed or reversed (handled by updatePaymentStatus). It can also be\n // written off as bad debt in unusual reconciliation flows.\n [InvoiceStatus.PAID]: [\n InvoiceStatus.DRAFT,\n InvoiceStatus.PARTIAL,\n InvoiceStatus.VIEWED,\n InvoiceStatus.SENT,\n InvoiceStatus.OVERDUE,\n InvoiceStatus.CANCELLED,\n InvoiceStatus.WRITTEN_OFF,\n ],\n // Terminal states: no outbound transitions.\n [InvoiceStatus.CANCELLED]: [],\n [InvoiceStatus.WRITTEN_OFF]: [],\n};\n\n/**\n * Module-scoped record of the status each Invoice instance was loaded with.\n * Used by the save-time status-transition guard to compare the prior\n * persisted status against the one being written, without adding a\n * persisted column. A WeakMap keyed by the instance keeps it out of the\n * schema and garbage-collects with the instance — same pattern LicenseSale\n * uses for its rights snapshot.\n */\nconst loadedInvoiceStatus = new WeakMap<Invoice, InvoiceStatus>();\n\n/**\n * Invoice represents a bill sent to a customer for goods or services.\n *\n * Invoices are distinct from Contracts - a Contract is an agreement,\n * while an Invoice is the billing document requesting payment.\n *\n * Invoices integrate with:\n * - `@happyvertical/smrt-ledgers` for revenue recognition (double-entry accounting)\n * - `@happyvertical/accounting` SDK for syncing with external providers (QBO, Stripe)\n *\n * @example\n * ```typescript\n * // Create an invoice\n * const invoice = await invoices.create({\n * customerId: customer.id,\n * invoiceNumber: await invoices.generateInvoiceNumber(),\n * issueDate: new Date(),\n * dueDate: addDays(new Date(), 30),\n * subtotal: 1000,\n * taxAmount: 50,\n * totalAmount: 1050,\n * currency: 'CAD'\n * });\n *\n * // Recognize revenue in ledger\n * await invoice.recognizeRevenue({\n * arAccountId: arAccount.id,\n * revenueAccountId: revenueAccount.id,\n * taxAccountId: taxAccount.id\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // ROOT FIX (S5 audit #1390 round 4): the generated create/update surface may\n // only set the billing-document descriptors (who/when/identifiers). The\n // derived/integrity amounts (`subtotal`, `taxAmount`, `totalAmount`,\n // `amountPaid`) and `status` are EXCLUDED — totals are derived from line\n // items, amountPaid from PaymentAllocations, and status from\n // updatePaymentStatus()/the lifecycle helpers. A caller can therefore create\n // a DRAFT invoice (customer / dueDate / line-item refs) but can never forge a\n // total, an amountPaid, or a PAID status through a generated route. Ledger\n // journal refs and communication timestamps are server-managed too.\n api: {\n include: ['list', 'get', 'create', 'update'],\n writable: [\n 'customerId',\n 'contractId',\n 'invoiceNumber',\n 'reference',\n 'issueDate',\n 'dueDate',\n 'currency',\n 'lineAmountType',\n 'taxRoundingScope',\n 'notes',\n 'customerNotes',\n 'terms',\n 'externalId',\n 'customerExternalId',\n 'externalProvider',\n ],\n },\n // NOTE: `send` and `recognizeRevenue` are intentionally NOT exposed over\n // MCP. `recognizeRevenue` posts a balanced journal into smrt-ledgers\n // (i.e. moves money in the books) and `send` transmits a billing document\n // to a customer — neither is safe as an unauthenticated/unguarded MCP tool.\n // Generated MCP tools carry no authz; financial mutations must go through\n // application code that enforces permissions. MCP create/update honor the\n // same `api.writable` allowlist, so status/amounts are unsettable over MCP\n // too. See S5 audit #1390.\n mcp: {\n include: ['list', 'get'],\n serializers: {\n get: {\n importPath: '@happyvertical/smrt-commerce',\n exportName: 'serializeInvoiceMcpGet',\n },\n },\n },\n cli: { skipApiCheck: true },\n})\nexport class Invoice extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-commerce\";\n /** @internal Collection read guard returns this snapshot on its root DB. */\n rebindMoneyRead(\n capability: DocumentMoneyReadRebindCapability,\n db: DatabaseInterface,\n ): this {\n if (capability !== DOCUMENT_MONEY_READ_REBIND)\n throw new MoneyValidationError(\n 'FROZEN_DOCUMENT',\n 'Invoice read rebinding requires the owning capability',\n );\n this._db = db;\n this.options.db = db;\n return this;\n }\n\n @field({ description: 'Invoice or customer credit note.' })\n kind: InvoiceKind = 'invoice';\n\n @foreignKey('Invoice', { nullable: true })\n creditedInvoiceId: string | null = null;\n\n /** Issued credit-note total cached under the source invoice lock. */\n @field({ ui: { format: 'money' } })\n creditedAmount = 0;\n\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global invoices\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Customer this invoice is for\n */\n @field({ description: 'The customer being billed.' })\n @foreignKey('Customer')\n customerId: string = '';\n\n /**\n * Optional link to Contract (cross-package reference)\n */\n @field({\n description: 'The order or agreement this invoice bills for, if any.',\n })\n @foreignKey('SalesContract', { validateSubtype: true })\n contractId: string = '';\n\n // ============================================================================\n // Identification\n // ============================================================================\n\n /**\n * Invoice number (e.g., INV-2025-0001)\n * Generated via InvoiceCollection.generateInvoiceNumber()\n */\n @field({ description: 'The number printed on the invoice.' })\n invoiceNumber: string = '';\n\n /**\n * External reference (e.g., customer PO number)\n */\n reference: string = '';\n\n // ============================================================================\n // Dates\n // ============================================================================\n\n /**\n * Date invoice was issued\n */\n @field({ description: 'The date the invoice was issued.' })\n issueDate: Date = new Date();\n\n /**\n * Payment due date\n */\n @field({ description: 'The date payment is expected.' })\n dueDate: Date = new Date();\n\n /**\n * Date invoice was fully paid\n */\n @field({ description: 'The date it was paid in full.' })\n paidDate: Date | null = null;\n\n // ============================================================================\n // Amounts\n // ============================================================================\n\n /**\n * Subtotal before tax, in **integer minor units** (cents, satoshis).\n *\n * The `= 0` initializer is load-bearing: SMRT maps an integer literal to\n * INTEGER and a decimal literal to DECIMAL. Money is exact, so it is stored\n * as minor units and never as a float — `$19.99` is `1999`, not `19.99`.\n * Writing a fractional major-unit value here is the bug; PostgreSQL rejects\n * it with `22P02` while SQLite's affinity silently stores it (#2361).\n */\n @field({ description: 'The amount before tax.', ui: { format: 'money' } })\n subtotal: number = 0;\n\n /**\n * Tax amount, in integer minor units (#2361). With line items this is the\n * sum of their rate-based tax plus {@link providerTaxAmount}.\n */\n @field({ description: 'The tax charged.', ui: { format: 'money' } })\n taxAmount: number = 0;\n\n /**\n * Tax calculated by the payment provider (for example Stripe Tax) from the\n * customer's tax location, in integer minor units (#3060). Server-managed:\n * billing-period close records it after the provider finalizes the invoice.\n * It is added to the line items' tax, so `taxAmount` and `totalAmount` stay\n * authoritative from line items plus this one provider figure.\n */\n @field({ ui: { format: 'money' } })\n providerTaxAmount: number = 0;\n\n /**\n * Total amount due (subtotal + tax), in integer minor units (#2361).\n */\n @field({\n description: 'What the customer owes in all, tax included.',\n ui: { format: 'money' },\n })\n totalAmount: number = 0;\n\n /**\n * Amount paid (sum of PaymentAllocations), in integer minor units (#2361).\n */\n @field({\n description: 'How much of the total has been paid so far.',\n ui: { format: 'money' },\n })\n amountPaid: number = 0;\n\n /**\n * Currency code (ISO 4217)\n */\n @field({\n description: 'The currency of the invoice.',\n ui: { widget: 'currency' },\n })\n currency: string = 'CAD';\n\n /** Pricing interpretation fixed for the document before issue. */\n lineAmountType: 'exclusive' | 'inclusive' | 'none' = 'exclusive';\n\n /** Component rounding scope fixed for the document before issue. */\n taxRoundingScope: 'line' | 'document' = 'line';\n\n /** Informational sum of resolved line discounts, in minor units. */\n @field({ ui: { format: 'money' } })\n discountTotal = 0;\n\n /** Immutable seller registration snapshot once the invoice leaves draft. */\n sellerTaxRegistrationsJson = '[]';\n\n get sellerTaxRegistrations(): TaxRegistrationOptions[] {\n try {\n const value: unknown = JSON.parse(this.sellerTaxRegistrationsJson);\n if (!Array.isArray(value)) return [];\n return value.filter(\n (entry): entry is TaxRegistrationOptions =>\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as TaxRegistrationOptions).taxComponentId ===\n 'string' &&\n typeof (entry as TaxRegistrationOptions).number === 'string' &&\n typeof (entry as TaxRegistrationOptions).label === 'string',\n );\n } catch {\n return [];\n }\n }\n\n set sellerTaxRegistrations(value: TaxRegistrationOptions[]) {\n this.sellerTaxRegistrationsJson = JSON.stringify(value);\n }\n\n /** Immutable customer tax identifier snapshot once issued. */\n partyTaxId = '';\n\n /** Stable issue/freeze identity, independent of lifecycle updated_at. */\n moneySnapshotId = '';\n\n // ============================================================================\n // Status\n // ============================================================================\n\n /**\n * Current invoice status\n */\n @field({\n description:\n 'Where the invoice stands: draft, sent, viewed, paid, overdue or cancelled.',\n })\n status: InvoiceStatus = InvoiceStatus.DRAFT;\n\n // ============================================================================\n // Ledger Integration\n // ============================================================================\n\n /**\n * Journal ID for AR recognition (cross-package ref to smrt-ledgers)\n */\n @crossPackageRef('@happyvertical/smrt-ledgers:Journal')\n arJournalId: string = '';\n\n /** Posted journal that exactly reverses arJournalId on cancellation. */\n @crossPackageRef('@happyvertical/smrt-ledgers:Journal', { nullable: true })\n arReversalJournalId: string | null = null;\n\n /**\n * Journal ID for revenue recognition (cross-package ref to smrt-ledgers)\n */\n @crossPackageRef('@happyvertical/smrt-ledgers:Journal')\n revenueJournalId: string = '';\n\n // ============================================================================\n // Provider Sync\n // ============================================================================\n\n /**\n * External ID in accounting provider (e.g., QBO invoice ID)\n */\n externalId: string = '';\n\n /**\n * Customer's external ID in accounting provider.\n * Used to link invoice to customer in external system.\n */\n customerExternalId: string = '';\n\n /**\n * Accounting provider name ('quickbooks' | 'stripe' | etc.)\n */\n externalProvider: string = '';\n\n /**\n * When invoice was last synced to provider\n */\n syncedAt: Date | null = null;\n\n /** Server-owned prepared/in-flight/result identity for frozen accounting sync. */\n @field({ sensitive: true })\n accountingSyncStateJson = '';\n\n /**\n * How the provider collects this invoice (#3190), decided once by billing\n * period close before its first push and reused on every retry, so it is\n * what the provider was asked for. Server-managed. A host must not offer\n * another payment rail on a `charge_automatically` invoice the provider is\n * still collecting.\n */\n collectionMethod: InvoiceCollectionMethod = '';\n\n // ============================================================================\n // Communication\n // ============================================================================\n\n /**\n * When invoice was sent to customer\n */\n sentAt: Date | null = null;\n\n /**\n * When customer viewed the invoice\n */\n viewedAt: Date | null = null;\n\n /**\n * Number of payment reminders sent\n */\n remindersSent: number = 0;\n\n /**\n * When last reminder was sent\n */\n lastReminderAt: Date | null = null;\n\n // ============================================================================\n // Notes\n // ============================================================================\n\n /**\n * Internal notes (not shown to customer)\n */\n @field({\n description: 'Private notes, not shown to the customer.',\n ui: { widget: 'textarea' },\n })\n notes: string = '';\n\n /**\n * Notes shown to customer on invoice\n */\n @field({\n description: 'A message shown to the customer on the invoice.',\n ui: { widget: 'textarea' },\n })\n customerNotes: string = '';\n\n /**\n * Payment terms text\n */\n @field({\n description: 'The payment conditions, such as Net 30.',\n ui: { widget: 'textarea' },\n })\n terms: string = '';\n\n constructor(options: InvoiceOptions = {}) {\n super(options);\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.creditedInvoiceId !== undefined)\n this.creditedInvoiceId = options.creditedInvoiceId;\n if (options.creditedAmount !== undefined)\n this.creditedAmount = options.creditedAmount;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.customerId !== undefined) this.customerId = options.customerId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.invoiceNumber !== undefined)\n this.invoiceNumber = options.invoiceNumber;\n if (options.reference !== undefined) this.reference = options.reference;\n if (options.issueDate !== undefined) this.issueDate = options.issueDate;\n if (options.dueDate !== undefined) this.dueDate = options.dueDate;\n if (options.paidDate !== undefined) this.paidDate = options.paidDate;\n if (options.subtotal !== undefined) this.subtotal = options.subtotal;\n if (options.taxAmount !== undefined) this.taxAmount = options.taxAmount;\n if (options.providerTaxAmount !== undefined)\n this.providerTaxAmount = options.providerTaxAmount;\n if (options.totalAmount !== undefined)\n this.totalAmount = options.totalAmount;\n if (options.amountPaid !== undefined) this.amountPaid = options.amountPaid;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.lineAmountType !== undefined)\n this.lineAmountType = options.lineAmountType;\n if (options.taxRoundingScope !== undefined)\n this.taxRoundingScope = options.taxRoundingScope;\n if (options.discountTotal !== undefined)\n this.discountTotal = options.discountTotal;\n if (options.sellerTaxRegistrationsJson !== undefined)\n this.sellerTaxRegistrationsJson = options.sellerTaxRegistrationsJson;\n if (options.sellerTaxRegistrations !== undefined)\n this.sellerTaxRegistrations = structuredClone(\n options.sellerTaxRegistrations,\n );\n if (options.partyTaxId !== undefined) this.partyTaxId = options.partyTaxId;\n if (options.moneySnapshotId !== undefined)\n this.moneySnapshotId = options.moneySnapshotId;\n if (options.status !== undefined) this.status = options.status;\n if (options.arJournalId !== undefined)\n this.arJournalId = options.arJournalId;\n if (options.arReversalJournalId !== undefined)\n this.arReversalJournalId = options.arReversalJournalId;\n if (options.revenueJournalId !== undefined)\n this.revenueJournalId = options.revenueJournalId;\n if (options.externalId !== undefined) this.externalId = options.externalId;\n if (options.customerExternalId !== undefined)\n this.customerExternalId = options.customerExternalId;\n if (options.externalProvider !== undefined)\n this.externalProvider = options.externalProvider;\n if (options.syncedAt !== undefined) this.syncedAt = options.syncedAt;\n if (options.accountingSyncStateJson !== undefined)\n this.accountingSyncStateJson = options.accountingSyncStateJson;\n if (options.collectionMethod !== undefined)\n this.collectionMethod = options.collectionMethod;\n if (options.sentAt !== undefined) this.sentAt = options.sentAt;\n if (options.viewedAt !== undefined) this.viewedAt = options.viewedAt;\n if (options.remindersSent !== undefined)\n this.remindersSent = options.remindersSent;\n if (options.lastReminderAt !== undefined)\n this.lastReminderAt = options.lastReminderAt;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.customerNotes !== undefined)\n this.customerNotes = options.customerNotes;\n if (options.terms !== undefined) this.terms = options.terms;\n }\n\n /**\n * Capture the persisted status the row was loaded with, so the save-time\n * transition guard can reject illegal status flips made via raw field\n * assignment (mass-assignment on a generated update route, a stale caller,\n * etc.). Only rows that already exist in the database carry a \"prior\"\n * status — freshly-constructed (not-yet-saved) invoices have no prior and\n * may start in any status.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedInvoiceStatus.set(this, this.status);\n }\n return this;\n }\n\n // ============================================================================\n // Save-time financial-integrity guard (S5 audit #1390)\n // ============================================================================\n\n /**\n * Recompute and validate financial fields before every write so forged\n * totals, negative amounts, and illegal status flips can't be persisted via\n * raw mass-assignment on the generated CRUD routes.\n *\n * Behaviour:\n * - **Totals are authoritative from line items.** When the invoice is\n * persisted and has line items, `subtotal` / `taxAmount` / `totalAmount`\n * are recomputed from those items, overriding whatever the caller sent.\n * This blocks \"create real line items but claim a tiny total\" forgery.\n * - **Without line items**, the caller-supplied totals are accepted but the\n * `totalAmount === subtotal + taxAmount` arithmetic invariant is enforced.\n * - **amountPaid is derived/validated.** When persisted, it is recomputed\n * from PaymentAllocations rather than trusted from the caller. It may\n * never exceed `totalAmount`.\n * - **Whole minor units and non-negativity** are enforced on all four\n * amounts. The integer check runs FIRST (#2401), before the\n * `totalAmount === subtotal + taxAmount` invariant, so a caller who wrote\n * major units gets \"money is minor units\" rather than a confusing\n * arithmetic-mismatch error from float addition.\n * - **Status transitions** are validated against the prior persisted status.\n */\n override async save(): Promise<this> {\n const { saveInvoice } = await import('../document-money.js');\n return (await saveInvoice(this)) as this;\n }\n\n /** @internal Aggregate persistence only. */\n async saveDerived(\n capability: DocumentMoneyWriteCapability,\n options: { recomputeLegacy: boolean },\n ): Promise<this> {\n if (capability !== DOCUMENT_MONEY_WRITE)\n throw new Error('Invoice money write capability is required');\n await claimAllocationParent(this, 'invoice');\n const persisted = await this.isSaved();\n const prior = await this.resolvePriorStatus(persisted);\n\n this.assertAmountsAreMinorUnits();\n if (options.recomputeLegacy) await this.recomputeAmountsForSave(persisted);\n else if (persisted && this.id) await this.recomputeAmountPaid();\n else this.amountPaid = 0;\n this.assertNonNegativeAmounts();\n this.assertStatusTransition(prior);\n this.assertPaymentStatusConsistent();\n await this.assertDraftDeliveryHistory(persisted);\n\n await assertAllocationParent(this, 'invoice');\n const result = (await super.save()) as this;\n loadedInvoiceStatus.set(this, this.status);\n return result;\n }\n\n override async delete(\n options: { expectedUpdatedAt?: Date | string } = {},\n ): Promise<void> {\n const { deleteInvoice } = await import('../document-money.js');\n await deleteInvoice(this, options);\n }\n\n /** @internal Aggregate persistence only. */\n async deleteDerived(\n capability: DocumentMoneyWriteCapability,\n options: { expectedUpdatedAt?: Date | string } = {},\n ): Promise<void> {\n if (capability !== DOCUMENT_MONEY_WRITE)\n throw new Error('Invoice money write capability is required');\n await super.delete(options);\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status (S5 audit #1390 round 4). The\n * {@link loadedInvoiceStatus} WeakMap is only populated when {@link initialize}\n * hydrated the row, so a `create({ id: <existing>, _skipLoad: true })` upsert\n * yields an instance whose WeakMap entry is missing — trusting it would treat\n * the write as a brand-new row and skip the transition guard entirely. Read\n * the persisted row's status directly so a create-onto-existing is correctly\n * treated as an update. `undefined` means no row exists (genuinely new).\n */\n private async resolvePriorStatus(\n persisted: boolean,\n ): Promise<InvoiceStatus | undefined> {\n if (persisted && this.id) {\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n if (row && row.status != null) {\n return row.status as InvoiceStatus;\n }\n } catch {\n // DB not ready — fall through to the in-memory record.\n }\n }\n return loadedInvoiceStatus.get(this);\n }\n\n /**\n * Recompute subtotal/tax/total from line items (when present) and derive\n * amountPaid from PaymentAllocations. Falls back to the arithmetic invariant\n * when the invoice has no line items. Tolerant of smrt-ledgers being absent\n * (the dynamic imports stay inside the package).\n */\n private async recomputeAmountsForSave(persisted: boolean): Promise<void> {\n if (persisted && this.id) {\n const { InvoiceLineItemCollection } = await import(\n '../collections/InvoiceLineItemCollection.js'\n );\n const lineItemCollection = await InvoiceLineItemCollection.create({\n ...this.options,\n db: this.db,\n });\n const lineItems = await lineItemCollection.findByInvoice(this.id);\n\n if (lineItems.length > 0) {\n const subtotal = lineItems.reduce(\n (sum: number, item: InvoiceLineItem) => sum + item.getSubtotal(),\n 0,\n );\n const taxAmount =\n lineItems.reduce(\n (sum: number, item: InvoiceLineItem) => sum + item.getTaxAmount(),\n 0,\n ) + this.providerTaxAmount;\n // Totals are AUTHORITATIVE from the line items (S5 audit #1390). Any\n // caller-supplied subtotal / tax / total is ignored and overwritten —\n // that is the security goal (a forged total can never be persisted)\n // AND it self-heals a stale stored total (e.g. one left behind after a\n // line item changed). We deliberately do NOT throw on a caller\n // mismatch: throwing blocked legitimate self-heal of stale totals, and\n // since the values are overwritten regardless, the throw added no\n // protection. Non-negativity is still enforced downstream by\n // assertNonNegativeAmounts().\n const computedTotal = subtotal + taxAmount;\n this.subtotal = subtotal;\n this.taxAmount = taxAmount;\n this.totalAmount = computedTotal;\n } else {\n this.assertTotalArithmetic();\n }\n\n // amountPaid is the sum of PaymentAllocations — never trust the caller.\n await this.recomputeAmountPaid();\n } else {\n // Not-yet-persisted invoice: no line items / allocations exist yet, so\n // enforce the arithmetic invariant on the caller-supplied totals.\n this.assertTotalArithmetic();\n\n // A brand-new invoice cannot already be paid: there are no\n // PaymentAllocations behind it yet, so a caller-supplied amountPaid (or a\n // PAID/PARTIAL starting status) would be unbacked money. Force it to\n // start unpaid; payment progress is only ever driven by allocations via\n // updatePaymentStatus(). (Re-derive on the first persisted save once\n // allocations exist.)\n this.amountPaid = 0;\n if (\n this.status === InvoiceStatus.PAID ||\n this.status === InvoiceStatus.PARTIAL\n ) {\n throw new Error(\n `Invoice ${this.invoiceNumber || '<new>'}: a new invoice cannot start ` +\n `in status '${this.status}' — payment status is derived from ` +\n 'PaymentAllocations, not set at creation. Create the invoice unpaid ' +\n '(DRAFT / SENT) and record payments via allocations.',\n );\n }\n }\n }\n\n private async recomputeAmountPaid(): Promise<void> {\n const { PaymentAllocationCollection } = await import(\n '../collections/PaymentAllocationCollection.js'\n );\n const allocationCollection = await PaymentAllocationCollection.create({\n ...this.options,\n db: this.db,\n });\n if (!this.id) throw new Error('Persisted invoice is missing its ID');\n this.amountPaid = await allocationCollection.getTotalAllocatedToInvoice(\n this.id,\n );\n }\n\n /**\n * Whether `amountPaid` covers `totalAmount`. This is the SINGLE source of\n * truth for the **PAID** decision — both {@link updatePaymentStatus} (which\n * decides PAID) and {@link assertPaymentStatusConsistent} (which validates\n * PAID on save) call it, so the PAID-deciding comparison and the\n * PAID-validating comparison can never drift apart (S5 audit #1390\n * follow-up).\n *\n * With integer minor units this is a plain `>=` (#2401). The float-era\n * hazard it used to guard against — a total summed from `0.1 × 3` line items\n * reading as \"not covered\" by `>=` but \"covered\" by an epsilon-tolerant\n * guard, leaving a genuinely-paid invoice unsaveable — cannot arise when both\n * sides are exact integers. That also removes the documented PAID/PARTIAL\n * asymmetry: {@link updatePaymentStatus} and {@link isPartiallyPaid} now\n * agree that any `amountPaid > 0` is a partial payment, because there is no\n * such thing as sub-cent dust.\n */\n private isFullyPaid(): boolean {\n return this.amountPaid + this.creditedAmount >= this.totalAmount;\n }\n\n /**\n * Whether `amountPaid` is a non-trivial partial payment of `totalAmount`.\n * Derived from {@link isFullyPaid} so the two stay mutually exclusive.\n */\n private isPartiallyPaid(): boolean {\n return this.amountPaid + this.creditedAmount > 0 && !this.isFullyPaid();\n }\n\n /**\n * After amounts are recomputed and amountPaid is re-derived from allocations,\n * assert the persisted `status` is consistent with the derived\n * amountPaid-vs-totalAmount relationship. This blocks the raw\n * `SENT → PAID with amountPaid=0` flip (and its inverse, claiming PARTIAL/SENT\n * while fully allocated) that the status-transition map alone permits because\n * SENT → PAID is a structurally legal edge.\n *\n * Only enforced for the payment-derived statuses (PAID / PARTIAL and the\n * unpaid states DRAFT / SENT / VIEWED). Lifecycle statuses that aren't a\n * function of amountPaid — OVERDUE, CANCELLED, WRITTEN_OFF — are left\n * to their own transition rules.\n */\n private assertPaymentStatusConsistent(): void {\n const label = this.invoiceNumber || this.id || '<new>';\n const fullyPaid = this.isFullyPaid();\n const partiallyPaid = this.isPartiallyPaid();\n\n if (this.status === InvoiceStatus.DRAFT && this.amountPaid !== 0) {\n throw new Error(\n `Invoice ${label}: DRAFT requires zero derived payment allocations.`,\n );\n }\n if (this.status === InvoiceStatus.PAID && !fullyPaid) {\n throw new Error(\n `Invoice ${label}: status is PAID but derived amountPaid ${this.amountPaid} ` +\n `does not cover totalAmount ${this.totalAmount}. Payment status is ` +\n 'derived from PaymentAllocations — use updatePaymentStatus().',\n );\n }\n if (this.status === InvoiceStatus.PARTIAL && !partiallyPaid) {\n throw new Error(\n `Invoice ${label}: status is PARTIAL but derived amountPaid ${this.amountPaid} ` +\n `is not a partial payment of totalAmount ${this.totalAmount}. Payment ` +\n 'status is derived from PaymentAllocations — use updatePaymentStatus().',\n );\n }\n // Claiming an unpaid open status (SENT / VIEWED) while the invoice is in\n // fact fully covered by allocations is also inconsistent.\n if (\n (this.status === InvoiceStatus.SENT ||\n this.status === InvoiceStatus.VIEWED) &&\n this.totalAmount > 0 &&\n fullyPaid\n ) {\n throw new Error(\n `Invoice ${label}: status is '${this.status}' but derived amountPaid ` +\n `${this.amountPaid} fully covers totalAmount ${this.totalAmount}. ` +\n 'Use updatePaymentStatus() so the status reflects the allocations.',\n );\n }\n }\n\n /** A payment reversal cannot erase an actual delivery event. */\n private async assertDraftDeliveryHistory(persisted: boolean): Promise<void> {\n if (this.status !== InvoiceStatus.DRAFT) return;\n if (this.sentAt || this.viewedAt) {\n throw new Error('DRAFT requires an undelivered invoice');\n }\n if (persisted && this.id) {\n const { InvoiceCollection } = await import(\n '../collections/InvoiceCollection.js'\n );\n const invoices = await InvoiceCollection.create({ db: this.db });\n const prior = await invoices.get({ id: this.id }, { cache: false });\n if (prior?.sentAt || prior?.viewedAt) {\n throw new Error(\n 'DRAFT cannot erase persisted invoice delivery history',\n );\n }\n }\n }\n\n /**\n * Enforce `totalAmount === subtotal + taxAmount` exactly. Used when the\n * invoice has no line items to recompute from.\n *\n * Exact because every term is integer minor units (#2401). The float era\n * needed a `0.01` tolerance here and then had to SNAP `totalAmount` to the\n * recomputed value afterwards, because a total that passed the invoice's\n * loose tolerance could still fail smrt-ledgers' tighter `BALANCE_EPSILON`\n * and void the journal `recognizeRevenue` builds from it. Integer arithmetic\n * removes both halves of that workaround: what the caller supplied either is\n * the sum or is rejected, and any journal built from it balances exactly.\n */\n private assertTotalArithmetic(): void {\n const expected = this.subtotal + this.taxAmount;\n if (this.totalAmount !== expected) {\n throw new Error(\n `Invoice ${this.invoiceNumber || this.id || '<new>'}: totalAmount ${this.totalAmount} ` +\n `must equal subtotal + taxAmount (${expected}).`,\n );\n }\n }\n\n /**\n * Reject amounts that are not whole minor units, negative financial values,\n * and an amountPaid that exceeds the total (overpayment is modelled\n * elsewhere, not by letting amountPaid float above the invoice total).\n */\n private assertAmountsAreMinorUnits(): void {\n assertIntegerMinorUnits(\n 'Invoice',\n this.invoiceNumber || this.id || '<new>',\n [\n ['subtotal', this.subtotal],\n ['taxAmount', this.taxAmount],\n ['providerTaxAmount', this.providerTaxAmount],\n ['totalAmount', this.totalAmount],\n ['amountPaid', this.amountPaid],\n ['creditedAmount', this.creditedAmount],\n ],\n );\n }\n\n private assertNonNegativeAmounts(): void {\n const label = this.invoiceNumber || this.id || '<new>';\n const amounts = [\n ['subtotal', this.subtotal],\n ['taxAmount', this.taxAmount],\n ['providerTaxAmount', this.providerTaxAmount],\n ['totalAmount', this.totalAmount],\n ['amountPaid', this.amountPaid],\n ['creditedAmount', this.creditedAmount],\n ] as const;\n // Re-checked after the recompute: line-item sums and the allocation total\n // feed these, so integer-ness has to hold for the derived values too — it\n // is what lets every comparison below drop its epsilon.\n assertIntegerMinorUnits('Invoice', label, amounts);\n for (const [field, value] of amounts) {\n if (value < 0) {\n throw new Error(\n `Invoice ${label}: ${field} must be a non-negative number (got ${value}).`,\n );\n }\n }\n if (this.amountPaid > this.totalAmount) {\n throw new Error(\n `Invoice ${label}: amountPaid ${this.amountPaid} cannot exceed totalAmount ${this.totalAmount}.`,\n );\n }\n }\n\n /**\n * Reject an illegal status flip done via raw field assignment. Compares the\n * about-to-be-written status against the status the row was loaded with.\n * No-op transitions (status unchanged) and brand-new rows are always allowed.\n */\n private assertStatusTransition(prior: InvoiceStatus | undefined): void {\n if (prior === undefined) return; // new row — any starting status is fine\n if (prior === this.status) return; // no-op re-save\n const allowed = INVOICE_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Invoice ${this.invoiceNumber || this.id}: illegal status transition ` +\n `'${prior}' → '${this.status}'. Use the guarded transition helpers ` +\n '(markSent / markViewed / updatePaymentStatus / cancel / writeOff).',\n );\n }\n }\n\n // ============================================================================\n // Status Helpers\n // ============================================================================\n\n /**\n * Check if invoice is a draft\n */\n isDraft(): boolean {\n return this.status === InvoiceStatus.DRAFT;\n }\n\n /**\n * Check if invoice has been sent\n */\n isSent(): boolean {\n return this.status === InvoiceStatus.SENT;\n }\n\n /**\n * Check if invoice is fully paid\n */\n isPaid(): boolean {\n return this.status === InvoiceStatus.PAID;\n }\n\n /**\n * Check if invoice is partially paid\n */\n isPartial(): boolean {\n return this.status === InvoiceStatus.PARTIAL;\n }\n\n /**\n * Check if invoice is overdue\n */\n isOverdue(): boolean {\n if (this.status === InvoiceStatus.OVERDUE) return true;\n if (this.isPaid() || this.status === InvoiceStatus.CANCELLED) return false;\n return new Date() > this.dueDate;\n }\n\n /**\n * Get remaining amount due\n */\n getAmountDue(): number {\n return Math.max(0, this.getBalance());\n }\n\n /** Signed amount still owed after payments and issued credit notes. */\n getBalance(): number {\n return this.totalAmount - this.amountPaid - this.creditedAmount;\n }\n\n // ============================================================================\n // Status Transitions\n // ============================================================================\n\n /**\n * Mark invoice as sent\n */\n markSent(): void {\n // Payment progress and actual delivery are independent. A prepaid invoice\n // may be sent later; preserve its allocation-derived status and first send.\n if (\n this.status === InvoiceStatus.PARTIAL ||\n this.status === InvoiceStatus.PAID\n ) {\n this.sentAt ??= new Date();\n return;\n }\n if (this.status !== InvoiceStatus.DRAFT) {\n throw new Error(\n `Cannot send invoice with status '${this.status}'. Only draft or payment-progress invoices can be sent.`,\n );\n }\n this.status = InvoiceStatus.SENT;\n this.sentAt = new Date();\n }\n\n /**\n * Record that the invoice was viewed by the customer.\n *\n * This method is idempotent - calling it multiple times will only\n * record the first view time. Status only transitions to VIEWED if\n * the current status is SENT (other statuses like PARTIAL or PAID\n * are preserved).\n */\n markViewed(): void {\n if (this.viewedAt) return; // Already viewed\n this.viewedAt = new Date();\n if (this.status === InvoiceStatus.SENT) {\n this.status = InvoiceStatus.VIEWED;\n }\n }\n\n /**\n * Update payment amount and status.\n *\n * Called when PaymentAllocations change. Handles both forward transitions\n * (DRAFT/SENT → PARTIAL → PAID) and reversals (PAID → PARTIAL → original status).\n *\n * Terminal statuses (CANCELLED, WRITTEN_OFF) are not modified.\n */\n updatePaymentStatus(amountPaid: number): void {\n this.amountPaid = amountPaid;\n\n // Do not change payment status for terminal states\n if (\n this.status === InvoiceStatus.CANCELLED ||\n this.status === InvoiceStatus.WRITTEN_OFF\n ) {\n return;\n }\n\n // Use the SAME epsilon-tolerant \"fully paid\" test the save-time guard\n // (assertPaymentStatusConsistent) applies, so a float-summed total paid\n // exactly (e.g. 0.1×3 line items paid 0.3) is treated as PAID by both —\n // not PARTIAL here and then rejected on save (S5 audit #1390 follow-up).\n if (this.isFullyPaid()) {\n if (this.status !== InvoiceStatus.PAID || !this.paidDate) {\n this.paidDate = new Date();\n }\n this.status = InvoiceStatus.PAID;\n } else if (amountPaid + this.creditedAmount > 0) {\n this.status = InvoiceStatus.PARTIAL;\n this.paidDate = null; // Clear paidDate if no longer fully paid\n } else {\n // No payment remaining: revert to the appropriate pre-payment status\n this.paidDate = null;\n if (this.viewedAt) {\n this.status = InvoiceStatus.VIEWED;\n } else if (this.sentAt) {\n this.status = InvoiceStatus.SENT;\n } else {\n this.status = InvoiceStatus.DRAFT;\n }\n }\n }\n\n /**\n * Cancel the invoice\n */\n cancel(): void {\n if (\n (this.kind === 'invoice' && this.isPaid()) ||\n (this.kind === 'credit_note' && this.amountPaid > 0)\n ) {\n throw new Error(\n `Cannot cancel ${this.kind} with status '${this.status}' and refunded/paid amount ${this.amountPaid}.`,\n );\n }\n this.status = InvoiceStatus.CANCELLED;\n }\n\n /** Cancel a posted money-v2 invoice with an exact atomic journal reversal. */\n async cancelWithRevenueReversal(\n options: CancelRevenueOptions,\n ): Promise<Journal | null> {\n const { cancelInvoiceWithRevenueReversal } = await import(\n '../invoice-posting.js'\n );\n return cancelInvoiceWithRevenueReversal(this, options);\n }\n\n /**\n * Write off the invoice (bad debt)\n */\n writeOff(): void {\n this.status = InvoiceStatus.WRITTEN_OFF;\n }\n\n // ============================================================================\n // Ledger Integration\n // ============================================================================\n\n /**\n * Recognize revenue and create AR journal entry.\n *\n * Creates a balanced journal entry in smrt-ledgers:\n * - Debit: Accounts Receivable (assets increase)\n * - Credit: Revenue (revenue increases)\n * - Credit: Tax Payable (liability increases, if taxAmount > 0)\n *\n * **Note**: This method saves the invoice after setting arJournalId.\n *\n * @param options - Account IDs for the journal entry\n * @returns The created journal\n *\n * @example\n * ```typescript\n * const journal = await invoice.recognizeRevenue({\n * arAccountId: '1120',\n * revenueAccountId: '4100',\n * taxAccountId: '2130'\n * });\n * ```\n */\n async recognizeRevenue(options: RecognizeRevenueOptions): Promise<Journal> {\n const { recognizeInvoiceRevenueV2 } = await import('../invoice-posting.js');\n const v2Journal = await recognizeInvoiceRevenueV2(this, options);\n if (v2Journal) return v2Journal;\n if (this.arJournalId) {\n throw new Error(\n `Revenue already recognized for invoice ${this.invoiceNumber} (journal ID: ${this.arJournalId})`,\n );\n }\n\n // Recompute/validate the invoice's authoritative totals BEFORE building any\n // journal (S5 audit #1390 round 2). Round 1 built and posted the journal\n // off stale/forged in-memory totals, then called save() — which recomputes\n // from line items and could throw, leaving an orphaned posted journal with\n // the wrong amount already in the books. Recomputing first means the\n // journal is built from the same authoritative figures save() would accept,\n // and a forged total is rejected before any ledger mutation happens.\n // Route through the tenant money gate before journal creation. In v2 this\n // recomputes the complete aggregate; in legacy mode it preserves the\n // existing calculation. Either way the journal reads persisted-authority\n // totals and migration maintenance blocks before any ledger write.\n await this.save();\n\n if (this.totalAmount <= 0) {\n throw new Error(\n `Cannot recognize revenue for invoice ${this.invoiceNumber} with non-positive total: ${this.totalAmount}`,\n );\n }\n\n if (this.taxAmount > 0 && !options.taxAccountId) {\n throw new Error(\n `Invoice ${this.invoiceNumber} has taxAmount ${this.taxAmount} but no taxAccountId provided`,\n );\n }\n\n // Dynamic import to avoid hard dependency on smrt-ledgers\n const { JournalCollection } = await import('@happyvertical/smrt-ledgers');\n\n const journalCollection = await JournalCollection.create(this.options);\n\n // Build entries array from the now-authoritative totals.\n const entries: JournalEntryData[] = [\n // Debit AR (assets increase)\n {\n accountId: options.arAccountId,\n debit: this.totalAmount,\n memo: `Invoice ${this.invoiceNumber}`,\n },\n // Credit Revenue\n {\n accountId: options.revenueAccountId,\n credit: this.subtotal,\n memo: `Invoice ${this.invoiceNumber}`,\n },\n ];\n\n // Credit Tax Payable if there's tax\n if (this.taxAmount > 0 && options.taxAccountId) {\n entries.push({\n accountId: options.taxAccountId,\n credit: this.taxAmount,\n memo: `Tax on Invoice ${this.invoiceNumber}`,\n });\n }\n\n // Create journal with entries\n const journal = await journalCollection.createWithEntries({\n description: `Revenue: Invoice ${this.invoiceNumber}`,\n sourceModule: 'smrt-commerce',\n sourceRef: this.id,\n entries,\n });\n\n // Post the journal, then link + persist the invoice as one logical unit. If\n // the invoice save fails after the journal is posted, void the journal so\n // the books don't retain a posted-but-unlinked entry (compensating action —\n // smrt-ledgers has no cross-row transaction primitive, and a posted journal\n // is immutable except via void()).\n await journal.post();\n // A posted journal always carries an id; fail fast rather than persist an\n // empty-string FK if that invariant breaks.\n if (!journal.id) {\n throw new Error(\n 'Invoice.recognizeRevenue: journal was posted but has no id',\n );\n }\n this.arJournalId = journal.id;\n try {\n await this.save();\n } catch (err) {\n try {\n await journal.void(\n `Reverted: invoice ${this.invoiceNumber} save failed during revenue recognition`,\n );\n } catch {\n // Best-effort compensation; surface the original failure regardless.\n }\n this.arJournalId = '';\n throw err;\n }\n\n return journal;\n }\n\n /**\n * Get the AR journal entry (if revenue was recognized)\n */\n async getArJournal(): Promise<Journal | null> {\n if (!this.arJournalId) return null;\n\n try {\n const { JournalCollection } = await import('@happyvertical/smrt-ledgers');\n const collection = await JournalCollection.create(this.options);\n return await collection.get({ id: this.arJournalId });\n } catch {\n // smrt-ledgers not available\n return null;\n }\n }\n\n // ============================================================================\n // Provider Sync\n // ============================================================================\n\n /**\n * Convert to InvoiceInput for SDK accounting provider sync.\n *\n * Fetches line items and maps all fields to the format expected\n * by @happyvertical/accounting providers. The SDK takes currency **major**\n * units (`12.34` USD), so every amount is converted from this package's\n * integer minor units with the currency's ISO 4217 exponent (#3060).\n *\n * @returns InvoiceInput compatible with @happyvertical/accounting\n *\n * @example\n * ```typescript\n * const provider = await getAccountingProvider({ type: 'quickbooks', ... });\n * const input = await invoice.toAccountingInput();\n * const result = await provider.invoices.push(input);\n * invoice.externalId = result.externalId;\n * invoice.syncedAt = result.syncedAt;\n * await invoice.save();\n * ```\n */\n async toAccountingInput(\n options?: FrozenInvoiceAccountingOptions,\n ): Promise<AccountingInvoiceInput> {\n if (!this.tenantId) return this.toAccountingInputLocked(options, null);\n const { withTenantMoneyRuntime } = await import(\n '../tenant-money-barrier.js'\n );\n return withTenantMoneyRuntime(\n this.db,\n this.tenantId,\n 'legacy-or-v2',\n (scope) =>\n this.withDatabase(scope.db, (bound) =>\n bound.toAccountingInputLocked(options, scope.barrier.settings),\n ),\n );\n }\n\n private async toAccountingInputLocked(\n options: FrozenInvoiceAccountingOptions | undefined,\n settings: { moneyModel: string } | null,\n ): Promise<AccountingInvoiceInput> {\n if (settings?.moneyModel === 'v2') {\n if (!options) {\n throw new MoneyValidationError(\n 'INVALID_MONEY_INPUT',\n 'Money-v2 accounting conversion requires explicit realm mapping',\n );\n }\n if (!this.id || !this.tenantId) {\n throw new MoneyValidationError(\n 'INVALID_MONEY_INPUT',\n 'Money-v2 accounting conversion requires a persisted tenant invoice',\n );\n }\n const { toFrozenInvoiceAccountingInput } = await import(\n '../invoice-accounting-sync.js'\n );\n return toFrozenInvoiceAccountingInput({\n db: this.db,\n tenantId: this.tenantId,\n invoiceId: this.id,\n mapping: options.mapping,\n });\n }\n // Dynamically import to avoid circular dependency\n const { InvoiceLineItemCollection } = await import(\n '../collections/InvoiceLineItemCollection.js'\n );\n\n const lineItemCollection = await InvoiceLineItemCollection.create(\n this.options,\n );\n // A synced invoice is always persisted; fail fast rather than query line\n // items under an empty id or push an invalid invoice to the provider.\n if (!this.id) {\n throw new Error(\n 'Invoice.toAccountingInput requires a persisted invoice (missing id)',\n );\n }\n const invoiceId = this.id;\n const lineItems = await lineItemCollection.findByInvoice(invoiceId);\n\n return {\n id: invoiceId,\n externalId: this.externalId || undefined,\n invoiceNumber: this.invoiceNumber,\n customerId: this.customerId,\n customerExternalId: this.customerExternalId || undefined,\n issueDate: this.issueDate,\n dueDate: this.dueDate,\n lineItems: lineItems.map((item: InvoiceLineItem) =>\n item.toAccountingLineItem(this.currency),\n ),\n subtotal: minorToMajorUnits(this.subtotal, this.currency),\n taxAmount: minorToMajorUnits(this.taxAmount, this.currency),\n totalAmount: minorToMajorUnits(this.totalAmount, this.currency),\n currency: this.currency,\n reference: this.reference || undefined,\n memo: this.customerNotes || undefined,\n };\n }\n}\n\nexport default Invoice;\n","/**\n * InvoiceCollection - Collection manager for Invoice objects\n * @packageDocumentation\n */\n\nimport {\n SmrtCollection,\n type SmrtListOptions,\n type SmrtObject,\n type SmrtSelectedRow,\n type SmrtSelectField,\n type SmrtStiReadScope,\n type SmrtWhereClause,\n} from '@happyvertical/smrt-core';\nimport {\n getTenantId,\n queryGlobal,\n queryWithGlobals,\n} from '@happyvertical/smrt-tenancy';\nimport { DOCUMENT_MONEY_READ_REBIND } from '../document-money-internal.js';\nimport { Invoice } from '../models/Invoice.js';\nimport { withTenantMoneyCollectionRead } from '../tenant-money-barrier.js';\nimport { InvoiceStatus } from '../types/index.js';\n\n/**\n * Statuses that indicate an invoice has outstanding balance.\n * Used for calculating outstanding amounts and finding unpaid invoices.\n */\nexport const UNPAID_STATUSES = [\n InvoiceStatus.SENT,\n InvoiceStatus.VIEWED,\n InvoiceStatus.PARTIAL,\n InvoiceStatus.OVERDUE,\n] as const;\n\n/**\n * Options for generating invoice numbers\n */\nexport interface InvoiceNumberOptions {\n /** Prefix for invoice numbers (default: 'INV') */\n prefix?: string;\n /** Format: 'prefix-year-seq' or 'prefix-seq' (default: 'prefix-year-seq') */\n format?: 'prefix-year-seq' | 'prefix-seq';\n}\n\nexport class InvoiceCollection extends SmrtCollection<Invoice> {\n static readonly _itemClass = Invoice;\n\n private async withMoneyRead<T>(operation: () => Promise<T>): Promise<T> {\n const rootDb = this.db;\n const tenantId = getTenantId();\n if (!tenantId) return operation();\n const result = await withTenantMoneyCollectionRead(\n rootDb,\n tenantId,\n (boundDb) => this.withDatabase(boundDb, operation),\n );\n if (result instanceof Invoice)\n result.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);\n else if (Array.isArray(result))\n for (const item of result)\n if (item instanceof Invoice)\n item.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);\n return result;\n }\n\n override async get(\n filter: string | SmrtWhereClause<Invoice>,\n options: Parameters<SmrtCollection<Invoice>['get']>[1] = {},\n ): Promise<Invoice | null> {\n return this.withMoneyRead(() => super.get(filter, options));\n }\n\n override async list<const Select extends readonly SmrtSelectField<Invoice>[]>(\n options: Omit<SmrtListOptions<Invoice>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<Record<string, unknown>[]>;\n override async list<const Select extends readonly SmrtSelectField<Invoice>[]>(\n options: Omit<SmrtListOptions<Invoice>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope?: undefined;\n },\n ): Promise<SmrtSelectedRow<Invoice, Select>[]>;\n override async list(\n options: Omit<SmrtListOptions<Invoice>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<SmrtObject[]>;\n override async list(options?: undefined): Promise<Invoice[]>;\n override async list(\n options: Omit<SmrtListOptions<Invoice>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope?: undefined;\n },\n ): Promise<Invoice[]>;\n override async list(\n options:\n | (Omit<SmrtListOptions<Invoice>, 'select'> & { select?: undefined })\n | undefined,\n ): Promise<SmrtObject[]>;\n override async list(\n options: SmrtListOptions<Invoice> | undefined,\n ): Promise<SmrtObject[] | Record<string, unknown>[]>;\n override async list(\n options: SmrtListOptions<Invoice> = {},\n ): Promise<SmrtObject[] | Record<string, unknown>[]> {\n return this.withMoneyRead(() => super.list(options));\n }\n\n /**\n * Find invoices by customer\n *\n * @param customerId - Customer ID\n * @returns Array of invoices\n */\n async findByCustomer(customerId: string): Promise<Invoice[]> {\n return await this.list({\n where: { customerId },\n orderBy: 'issueDate DESC',\n });\n }\n\n /**\n * Find invoices by contract\n *\n * @param contractId - Contract ID\n * @returns Array of invoices\n */\n async findByContract(contractId: string): Promise<Invoice[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'issueDate DESC',\n });\n }\n\n /**\n * Find invoices by status\n *\n * @param status - Invoice status\n * @returns Array of invoices\n */\n async findByStatus(status: InvoiceStatus): Promise<Invoice[]> {\n return await this.list({\n where: { status },\n orderBy: 'issueDate DESC',\n });\n }\n\n /**\n * Find all draft invoices\n *\n * @returns Array of draft invoices\n */\n async findDrafts(): Promise<Invoice[]> {\n return await this.findByStatus(InvoiceStatus.DRAFT);\n }\n\n /**\n * Find all sent invoices\n *\n * @returns Array of sent invoices\n */\n async findSent(): Promise<Invoice[]> {\n return await this.findByStatus(InvoiceStatus.SENT);\n }\n\n /**\n * Find all paid invoices\n *\n * @returns Array of paid invoices\n */\n async findPaid(): Promise<Invoice[]> {\n return await this.findByStatus(InvoiceStatus.PAID);\n }\n\n /**\n * Find all partial invoices\n *\n * @returns Array of partially paid invoices\n */\n async findPartial(): Promise<Invoice[]> {\n return await this.findByStatus(InvoiceStatus.PARTIAL);\n }\n\n /**\n * Find overdue invoices (past due date, not paid).\n *\n * **Note**: This method finds invoices by date, but does not automatically\n * update their status to OVERDUE. Use a background job or scheduled task\n * to periodically update invoice statuses based on due dates.\n *\n * @returns Array of overdue invoices\n */\n async findOverdue(): Promise<Invoice[]> {\n const now = new Date().toISOString();\n\n // Get invoices that are sent/viewed/partial and past due\n const candidates = await this.list({\n where: {\n 'dueDate <': now,\n },\n orderBy: 'dueDate ASC',\n });\n\n return candidates.filter((inv) =>\n (UNPAID_STATUSES as readonly InvoiceStatus[]).includes(inv.status),\n );\n }\n\n /**\n * Find invoices in date range (by issue date)\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of invoices\n */\n async findByDateRange(startDate: Date, endDate: Date): Promise<Invoice[]> {\n return await this.list({\n where: {\n 'issueDate >=': startDate.toISOString(),\n 'issueDate <=': endDate.toISOString(),\n },\n orderBy: 'issueDate DESC',\n });\n }\n\n /**\n * Find invoice by external ID (from accounting provider)\n *\n * @param externalId - External ID\n * @returns Invoice or null\n */\n async findByExternalId(externalId: string): Promise<Invoice | null> {\n const results = await this.list({\n where: { externalId },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Find invoices needing sync to external provider\n * (no externalId or stale syncedAt)\n *\n * @returns Array of invoices needing sync\n */\n async findNeedingSync(): Promise<Invoice[]> {\n // Get all non-draft invoices without externalId\n const allInvoices = await this.list({\n orderBy: 'issueDate DESC',\n });\n\n return allInvoices.filter(\n (inv) =>\n inv.status !== InvoiceStatus.DRAFT &&\n inv.status !== InvoiceStatus.CANCELLED &&\n !inv.externalId,\n );\n }\n\n /**\n * Find invoice by invoice number\n *\n * @param invoiceNumber - Invoice number\n * @returns Invoice or null\n */\n async findByNumber(invoiceNumber: string): Promise<Invoice | null> {\n const results = await this.list({\n where: { invoiceNumber },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Count invoices for a given year\n *\n * @param year - Year to count\n * @returns Number of invoices\n */\n async countForYear(year: number): Promise<number> {\n const startDate = new Date(year, 0, 1).toISOString();\n const endDate = new Date(year + 1, 0, 1).toISOString();\n\n const invoices = await this.list({\n where: {\n 'issueDate >=': startDate,\n 'issueDate <': endDate,\n },\n });\n\n return invoices.length;\n }\n\n /**\n * Generate the next invoice number.\n *\n * **Warning**: This method has a potential race condition if multiple\n * invoices are created concurrently. For high-concurrency environments,\n * consider using a database sequence or atomic counter instead.\n *\n * @param options - Optional configuration for number format\n * @returns Generated invoice number (e.g., 'INV-2025-0001')\n *\n * @example\n * ```typescript\n * // Default format: INV-2025-0001\n * const num = await invoices.generateInvoiceNumber();\n *\n * // Custom prefix: BILL-2025-0001\n * const num = await invoices.generateInvoiceNumber({ prefix: 'BILL' });\n *\n * // Simple sequential: INV-000001\n * const num = await invoices.generateInvoiceNumber({ format: 'prefix-seq' });\n * ```\n */\n async generateInvoiceNumber(\n options: InvoiceNumberOptions = {},\n ): Promise<string> {\n const prefix = options.prefix || 'INV';\n const format = options.format || 'prefix-year-seq';\n\n const year = new Date().getFullYear();\n\n if (format === 'prefix-year-seq') {\n const count = await this.countForYear(year);\n const seq = String(count + 1).padStart(4, '0');\n return `${prefix}-${year}-${seq}`;\n }\n\n // Simple sequential format\n const allInvoices = await this.list({});\n const seq = String(allInvoices.length + 1).padStart(6, '0');\n return `${prefix}-${seq}`;\n }\n\n /**\n * Get total outstanding (unpaid) amount for a customer\n *\n * @param customerId - Customer ID\n * @returns Total amount due\n */\n async getTotalOutstandingForCustomer(customerId: string): Promise<number> {\n const invoices = await this.list({\n where: { customerId },\n });\n\n return invoices\n .filter((inv) =>\n (UNPAID_STATUSES as readonly InvoiceStatus[]).includes(inv.status),\n )\n .reduce((sum, inv) => sum + inv.getAmountDue(), 0);\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all invoices belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of invoices for the tenant\n */\n async findByTenant(tenantId: string): Promise<Invoice[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global invoices (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global invoices\n */\n async findGlobal(): Promise<Invoice[]> {\n return queryGlobal<Invoice>(this);\n }\n\n /**\n * Find invoices for a tenant including global invoices.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID\n * @returns Array of tenant-specific and global invoices\n */\n async findWithGlobals(tenantId: string): Promise<Invoice[]> {\n return queryWithGlobals<Invoice>(this, tenantId, 'Invoice.findWithGlobals');\n }\n}\n"],"mappings":"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"}
|
package/dist/chunks/{InvoiceLineItemCollection-CHm9zdqh.js → InvoiceLineItemCollection-CPQGghyd.js}
RENAMED
|
@@ -2,8 +2,8 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
|
2
2
|
import { i as resolveInvoiceLineDraft, r as calculateInvoiceMinorLine } from "./calculations-DaHR17il.js";
|
|
3
3
|
import { o as minorToMajorUnits } from "./units-CNcjH_eE.js";
|
|
4
4
|
import { MoneyValidationError } from "../money.js";
|
|
5
|
-
import { a as DOCUMENT_MONEY_READ_REBIND, o as DOCUMENT_MONEY_WRITE } from "./TaxSettingsCollection-
|
|
6
|
-
import { i as withTenantMoneyCollectionRead } from "./tenant-money-barrier-
|
|
5
|
+
import { a as DOCUMENT_MONEY_READ_REBIND, o as DOCUMENT_MONEY_WRITE } from "./TaxSettingsCollection-DYYF-In-.js";
|
|
6
|
+
import { i as withTenantMoneyCollectionRead } from "./tenant-money-barrier-y6EAa6vY.js";
|
|
7
7
|
import { SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
8
8
|
import { TenantScoped, getTenantId, queryGlobal, queryWithGlobals, tenantId } from "@happyvertical/smrt-tenancy";
|
|
9
9
|
//#region src/models/InvoiceLineItem.ts
|
|
@@ -196,7 +196,7 @@ var InvoiceLineItem = class extends SmrtObject {
|
|
|
196
196
|
this.calculateAmount();
|
|
197
197
|
throw error;
|
|
198
198
|
}
|
|
199
|
-
const { saveInvoiceLineItem } = await import("./document-money-
|
|
199
|
+
const { saveInvoiceLineItem } = await import("./document-money-oUDvdjOf.js").then((n) => n.t);
|
|
200
200
|
return await saveInvoiceLineItem(this);
|
|
201
201
|
}
|
|
202
202
|
/** @internal Aggregate persistence only. */
|
|
@@ -208,7 +208,7 @@ var InvoiceLineItem = class extends SmrtObject {
|
|
|
208
208
|
return await super.save();
|
|
209
209
|
}
|
|
210
210
|
async delete(options = {}) {
|
|
211
|
-
const { deleteInvoiceLineItem } = await import("./document-money-
|
|
211
|
+
const { deleteInvoiceLineItem } = await import("./document-money-oUDvdjOf.js").then((n) => n.t);
|
|
212
212
|
await deleteInvoiceLineItem(this, options);
|
|
213
213
|
}
|
|
214
214
|
/** @internal Aggregate persistence only. */
|
|
@@ -259,16 +259,17 @@ __decorateClass([field({ description: "What is being billed on this line." })],
|
|
|
259
259
|
__decorateClass([crossPackageRef("@happyvertical/smrt-products:Product", { nullable: true })], InvoiceLineItem.prototype, "productId", 2);
|
|
260
260
|
__decorateClass([field({ description: "How many." })], InvoiceLineItem.prototype, "quantity", 2);
|
|
261
261
|
__decorateClass([field({
|
|
262
|
-
description: "The price of one, before tax.",
|
|
262
|
+
description: "The price of one, before discount; includes tax when the document's prices include tax.",
|
|
263
263
|
ui: { format: "money" }
|
|
264
264
|
})], InvoiceLineItem.prototype, "unitPrice", 2);
|
|
265
265
|
__decorateClass([field({ ui: { format: "money" } })], InvoiceLineItem.prototype, "discount", 2);
|
|
266
266
|
__decorateClass([field({ type: "decimal" })], InvoiceLineItem.prototype, "discountRate", 2);
|
|
267
267
|
__decorateClass([field({ description: "The stored fractional tax rate for this line (0.05 means 5%)." })], InvoiceLineItem.prototype, "taxRate", 2);
|
|
268
268
|
__decorateClass([foreignKey("TaxCode", { nullable: true })], InvoiceLineItem.prototype, "taxCodeId", 2);
|
|
269
|
+
__decorateClass([field({ ui: { format: "money" } })], InvoiceLineItem.prototype, "taxAmount", 2);
|
|
269
270
|
__decorateClass([field({ nullable: true })], InvoiceLineItem.prototype, "legacyAmount", 2);
|
|
270
271
|
__decorateClass([field({
|
|
271
|
-
description: "The line
|
|
272
|
+
description: "The line amount: net of discount, and including tax only when the document's prices include tax.",
|
|
272
273
|
ui: { format: "money" }
|
|
273
274
|
})], InvoiceLineItem.prototype, "amount", 2);
|
|
274
275
|
__decorateClass([crossPackageRef("@happyvertical/smrt-ledgers:Account", { nullable: true })], InvoiceLineItem.prototype, "revenueAccountId", 2);
|
|
@@ -464,4 +465,4 @@ var InvoiceLineItemCollection = class extends SmrtCollection {
|
|
|
464
465
|
//#endregion
|
|
465
466
|
export { InvoiceLineItemCollection_exports as n, InvoiceLineItem as r, InvoiceLineItemCollection as t };
|
|
466
467
|
|
|
467
|
-
//# sourceMappingURL=InvoiceLineItemCollection-
|
|
468
|
+
//# sourceMappingURL=InvoiceLineItemCollection-CPQGghyd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InvoiceLineItemCollection-CPQGghyd.js","names":[],"sources":["../../src/models/InvoiceLineItem.ts","../../src/collections/InvoiceLineItemCollection.ts"],"sourcesContent":["/**\n * InvoiceLineItem model - individual items on an invoice\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { DatabaseInterface } from '@happyvertical/smrt-core/migrations';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { minorToMajorUnits } from '../billing/units.js';\nimport {\n DOCUMENT_MONEY_READ_REBIND,\n DOCUMENT_MONEY_WRITE,\n type DocumentMoneyReadRebindCapability,\n type DocumentMoneyWriteCapability,\n} from '../document-money-internal.js';\nimport { MoneyValidationError } from '../money.js';\nimport {\n calculateInvoiceMinorLine,\n type InvoiceCalculationContext,\n type InvoiceLineDraft,\n resolveInvoiceLineDraft,\n} from '../svelte/invoices/calculations.js';\nimport type {\n AccountingLineItemInput,\n InvoiceCreditCalculationBasis,\n InvoiceLineEditorState,\n InvoiceLineItemOptions,\n InvoiceTaxComponentSnapshot,\n} from '../types/index.js';\n\n/**\n * InvoiceLineItem represents a single line item on an invoice.\n *\n * Line items contain details of what is being billed, including\n * quantity, pricing, and optional source tracking (e.g., from ad campaigns).\n *\n * @example\n * ```typescript\n * const lineItem = await lineItems.create({\n * invoiceId: invoice.id,\n * description: 'Display Advertising - Summer Campaign',\n * quantity: 50000, // impressions\n * unitPrice: 1, // 1 cent per impression (minor units)\n * taxRate: 0.05,\n * sourceType: 'campaign',\n * sourceId: 'campaign-uuid',\n * periodStart: new Date('2025-06-01'),\n * periodEnd: new Date('2025-06-30')\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: {\n include: ['list', 'get', 'create', 'update', 'delete'],\n writable: [\n 'invoiceId',\n 'description',\n 'sku',\n 'productId',\n 'quantity',\n 'unitPrice',\n 'discount',\n 'discountType',\n 'discountRate',\n 'taxRate',\n 'taxMode',\n 'taxCodeId',\n 'sourceType',\n 'sourceId',\n 'periodStart',\n 'periodEnd',\n 'revenueAccountId',\n 'sortOrder',\n ],\n },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class InvoiceLineItem extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-commerce\";\n /** @internal Collection read guard returns this snapshot on its root DB. */\n rebindMoneyRead(\n capability: DocumentMoneyReadRebindCapability,\n db: DatabaseInterface,\n ): this {\n if (capability !== DOCUMENT_MONEY_READ_REBIND)\n throw new MoneyValidationError(\n 'FROZEN_DOCUMENT',\n 'Invoice line read rebinding requires the owning capability',\n );\n this._db = db;\n this.options.db = db;\n return this;\n }\n\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global invoice line items\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent invoice\n */\n @field({ description: 'The invoice this line belongs to.' })\n @foreignKey('Invoice', { role: 'items' })\n invoiceId: string = '';\n\n @foreignKey('InvoiceLineItem', { nullable: true })\n creditedLineId: string | null = null;\n\n @field({\n description:\n 'Frozen source basis used to cap and reproduce an issued credit line.',\n })\n creditCalculationBasisJson = '';\n\n get creditCalculationBasis(): InvoiceCreditCalculationBasis | null {\n try {\n const value: unknown = JSON.parse(this.creditCalculationBasisJson);\n if (!value || typeof value !== 'object' || !('kind' in value))\n return null;\n const basis = value as Record<string, unknown>;\n if (\n (basis.kind !== 'quantity' && basis.kind !== 'net_amount') ||\n typeof basis.sourceQuantity !== 'string' ||\n !Number.isSafeInteger(basis.sourceUnitPrice) ||\n (basis.sourceDiscountType !== 'none' &&\n basis.sourceDiscountType !== 'percent' &&\n basis.sourceDiscountType !== 'amount') ||\n typeof basis.sourceDiscountRate !== 'string' ||\n !Number.isSafeInteger(basis.sourceDiscountMinor) ||\n (basis.kind === 'quantity' && typeof basis.quantity !== 'string') ||\n (basis.kind === 'net_amount' && !Number.isSafeInteger(basis.netMinor))\n )\n return null;\n return basis as unknown as InvoiceCreditCalculationBasis;\n } catch {\n return null;\n }\n }\n\n set creditCalculationBasis(value: InvoiceCreditCalculationBasis | null) {\n this.creditCalculationBasisJson = value ? JSON.stringify(value) : '';\n }\n\n /**\n * Item description\n */\n @field({ description: 'What is being billed on this line.' })\n description: string = '';\n\n /**\n * SKU or item code\n */\n sku: string = '';\n\n @crossPackageRef('@happyvertical/smrt-products:Product', { nullable: true })\n productId: string | null = null;\n\n /** Nonnegative decimal quantity (hours, weight, units), up to six fractional digits. */\n @field({ description: 'How many.' })\n quantity: number = 1.0;\n\n /** Raw editor modes retained only while they resolve to the authoritative model fields. */\n invoiceEditorStateJson: string = '';\n\n /** Guarded editor metadata view; malformed stored JSON is treated as absent. */\n get invoiceEditorState(): InvoiceLineEditorState | null {\n try {\n const value: unknown = JSON.parse(this.invoiceEditorStateJson);\n if (\n !value ||\n typeof value !== 'object' ||\n !('draft' in value) ||\n !('context' in value) ||\n !value.draft ||\n typeof value.draft !== 'object' ||\n !value.context ||\n typeof value.context !== 'object'\n )\n return null;\n return value as InvoiceLineEditorState;\n } catch {\n return null;\n }\n }\n\n /** Store only this model's editor state, preserving unrelated metadata. */\n set invoiceEditorState(value: InvoiceLineEditorState | null) {\n this.invoiceEditorStateJson = value ? JSON.stringify(value) : '';\n }\n\n /**\n * Unit price before discount, in **integer minor units** (cents, satoshis),\n * in the document's pricing mode: tax-inclusive when its `lineAmountType`\n * is `inclusive`.\n *\n * Money is exact, so it is stored as minor units and never as a float —\n * `$19.99` is `1999`. An integer literal is what maps this to an INTEGER\n * column (#2361).\n */\n @field({\n description:\n \"The price of one, before discount; includes tax when the document's prices include tax.\",\n ui: { format: 'money' },\n })\n unitPrice: number = 0;\n\n /**\n * Discount amount (flat, not percentage), in integer minor units (#2361).\n */\n @field({ ui: { format: 'money' } })\n discount: number = 0;\n\n discountType: 'none' | 'percent' | 'amount' = 'none';\n\n @field({ type: 'decimal' })\n discountRate = '0.0';\n\n /**\n * Tax rate as a decimal fraction (e.g., 0.05 for 5%).\n *\n * A genuine rate, not money: it is inherently fractional, so the `0.0`\n * initializer is load-bearing and maps it to a DECIMAL column. INTEGER would\n * truncate every rate to 0 (#2361).\n */\n @field({\n description:\n 'The stored fractional tax rate for this line (0.05 means 5%).',\n })\n taxRate: number = 0.0;\n\n taxMode: 'inherit' | 'override' = 'inherit';\n\n @foreignKey('TaxCode', { nullable: true })\n taxCodeId: string | null = null;\n\n taxCodeCode: string | null = null;\n\n taxCodeName: string | null = null;\n\n taxComponentsJson: string | null = null;\n\n get taxComponents(): InvoiceTaxComponentSnapshot[] {\n if (this.taxComponentsJson === null) return [];\n try {\n const value: unknown = JSON.parse(this.taxComponentsJson);\n if (!Array.isArray(value)) return [];\n return value.filter(\n (entry): entry is InvoiceTaxComponentSnapshot =>\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as InvoiceTaxComponentSnapshot).taxComponentId ===\n 'string' &&\n typeof (entry as InvoiceTaxComponentSnapshot).name === 'string' &&\n typeof (entry as InvoiceTaxComponentSnapshot).percent === 'string' &&\n Number.isSafeInteger((entry as InvoiceTaxComponentSnapshot).taxMinor),\n );\n } catch {\n return [];\n }\n }\n\n set taxComponents(value: InvoiceTaxComponentSnapshot[]) {\n this.taxComponentsJson = JSON.stringify(value);\n }\n\n /** Exact sum of frozen component taxes, in integer minor units. */\n @field({ ui: { format: 'money' } })\n taxAmount = 0;\n\n /**\n * @internal Pre-v2 line amount preserved only by the line-amount migration\n * for rollback; not a user-facing amount, so it carries no money hint.\n */\n @field({ nullable: true })\n legacyAmount: number | null = null;\n\n /** Stable document snapshot identity; ordinary lifecycle saves do not change it. */\n moneySnapshotId = '';\n\n /**\n * Calculated line amount, in integer minor units (#2361). Under money v2 it\n * is net of discount and before tax for `exclusive` / `none` documents, and\n * tax-inclusive for `inclusive` ones; {@link taxAmount} holds the tax. A\n * legacy-model tenant still stores qty * price - discount + tax.\n */\n @field({\n description:\n \"The line amount: net of discount, and including tax only when the document's prices include tax.\",\n ui: { format: 'money' },\n })\n amount: number = 0;\n\n // ============================================================================\n // Source Tracking\n // ============================================================================\n\n /**\n * Type of source ('campaign' | 'contract' | 'manual' | etc.)\n */\n sourceType: string = '';\n\n /**\n * ID of the source (e.g., campaign ID, contract ID)\n */\n sourceId: string = '';\n\n /**\n * Service period start (for time-based billing)\n */\n periodStart: Date | null = null;\n\n /**\n * Service period end (for time-based billing)\n */\n periodEnd: Date | null = null;\n\n // ============================================================================\n // Accounting\n // ============================================================================\n\n /**\n * Revenue account ID (cross-package ref to smrt-ledgers)\n * Used for revenue recognition to specific accounts\n */\n @crossPackageRef('@happyvertical/smrt-ledgers:Account', { nullable: true })\n revenueAccountId: string | null = null;\n\n /**\n * Sort order within the invoice\n */\n sortOrder: number = 0;\n\n constructor(options: InvoiceLineItemOptions = {}) {\n super(options);\n if (options.invoiceEditorStateJson !== undefined)\n this.invoiceEditorStateJson = options.invoiceEditorStateJson;\n if (options.invoiceEditorState !== undefined)\n this.invoiceEditorState = structuredClone(options.invoiceEditorState);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.invoiceId !== undefined) this.invoiceId = options.invoiceId;\n if (options.creditedLineId !== undefined)\n this.creditedLineId = options.creditedLineId;\n if (options.creditCalculationBasisJson !== undefined)\n this.creditCalculationBasisJson = options.creditCalculationBasisJson;\n if (options.creditCalculationBasis !== undefined)\n this.creditCalculationBasis = structuredClone(\n options.creditCalculationBasis,\n );\n if (options.description !== undefined)\n this.description = options.description;\n if (options.sku !== undefined) this.sku = options.sku;\n if (options.productId !== undefined) this.productId = options.productId;\n if (options.quantity !== undefined) this.quantity = options.quantity;\n if (options.unitPrice !== undefined) this.unitPrice = options.unitPrice;\n if (options.discount !== undefined) this.discount = options.discount;\n if (options.discountType !== undefined)\n this.discountType = options.discountType;\n if (options.discountRate !== undefined)\n this.discountRate = options.discountRate;\n if (options.taxRate !== undefined) this.taxRate = options.taxRate;\n if (options.taxMode !== undefined) this.taxMode = options.taxMode;\n if (options.taxCodeId !== undefined) this.taxCodeId = options.taxCodeId;\n if (options.taxCodeCode !== undefined)\n this.taxCodeCode = options.taxCodeCode;\n if (options.taxCodeName !== undefined)\n this.taxCodeName = options.taxCodeName;\n if (options.taxComponentsJson !== undefined)\n this.taxComponentsJson = options.taxComponentsJson;\n if (options.taxComponents !== undefined)\n this.taxComponents = structuredClone(options.taxComponents);\n if (options.taxAmount !== undefined) this.taxAmount = options.taxAmount;\n if (options.legacyAmount !== undefined)\n this.legacyAmount = options.legacyAmount;\n if (options.moneySnapshotId !== undefined)\n this.moneySnapshotId = options.moneySnapshotId;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.sourceType !== undefined) this.sourceType = options.sourceType;\n if (options.sourceId !== undefined) this.sourceId = options.sourceId;\n if (options.periodStart !== undefined)\n this.periodStart = options.periodStart;\n if (options.periodEnd !== undefined) this.periodEnd = options.periodEnd;\n if (options.revenueAccountId !== undefined)\n this.revenueAccountId = options.revenueAccountId;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n }\n\n /** Calculate exact safe integer-minor totals, rounding gross and tax ties toward positive infinity. */\n calculateAmount(): number {\n return calculateInvoiceMinorLine(this).totalMinor;\n }\n\n /** Rounded quantity times price, less the resolved flat minor-unit discount. */\n getSubtotal(): number {\n return calculateInvoiceMinorLine(this).subtotalMinor;\n }\n\n /** Tax on the rounded discounted subtotal, in safe integer minor units. */\n getTaxAmount(): number {\n return calculateInvoiceMinorLine(this).taxMinor;\n }\n\n /** Apply a server-validated draft without saving; authorization and transaction stay caller-owned. */\n applyEditorDraft(\n draft: InvoiceLineDraft,\n context: InvoiceCalculationContext,\n ): void {\n const resolved = resolveInvoiceLineDraft(draft, context);\n Object.assign(this, resolved);\n this.invoiceEditorState = structuredClone({ draft, context });\n }\n\n /** Recover original editing modes only when current authority and resolved model fields agree. */\n getEditorDraft(\n authorizedContext: InvoiceCalculationContext,\n ): InvoiceLineDraft | null {\n const state = this.invoiceEditorState;\n if (\n !state ||\n !authorizedContext ||\n state.context?.currency !== authorizedContext.currency ||\n state.context?.inheritedTaxRate !== authorizedContext.inheritedTaxRate\n )\n return null;\n try {\n const resolved = resolveInvoiceLineDraft(state.draft, authorizedContext);\n if (\n resolved.description !== this.description ||\n resolved.sku !== this.sku ||\n resolved.quantity !== this.quantity ||\n resolved.unitPrice !== this.unitPrice ||\n resolved.discount !== this.discount ||\n resolved.taxRate !== this.taxRate\n )\n return null;\n return structuredClone(state.draft);\n } catch {\n return null;\n }\n }\n\n /** Validate resolved fields and recompute amount before persistence; editor metadata never determines money. */\n override async save(): Promise<this> {\n try {\n void this.db;\n } catch (error) {\n // Preserve the legacy validation contract for detached model instances:\n // invalid arithmetic is reported before the ordinary initialization error.\n this.calculateAmount();\n throw error;\n }\n const { saveInvoiceLineItem } = await import('../document-money.js');\n return (await saveInvoiceLineItem(this)) as this;\n }\n\n /** @internal Aggregate persistence only. */\n async saveDerived(\n capability: DocumentMoneyWriteCapability,\n legacy: boolean,\n ): Promise<this> {\n if (capability !== DOCUMENT_MONEY_WRITE)\n throw new Error('Invoice line money write capability is required');\n if (legacy) this.amount = this.calculateAmount();\n const state = this.invoiceEditorState;\n if (\n this.invoiceEditorStateJson &&\n (!state || !this.getEditorDraft(state.context))\n )\n this.invoiceEditorState = null;\n return (await super.save()) as this;\n }\n\n override async delete(\n options: { expectedUpdatedAt?: Date | string } = {},\n ): Promise<void> {\n const { deleteInvoiceLineItem } = await import('../document-money.js');\n await deleteInvoiceLineItem(this, options);\n }\n\n /** @internal Aggregate persistence only. */\n async deleteDerived(\n capability: DocumentMoneyWriteCapability,\n options: { expectedUpdatedAt?: Date | string } = {},\n ): Promise<void> {\n if (capability !== DOCUMENT_MONEY_WRITE)\n throw new Error('Invoice line money write capability is required');\n await super.delete(options);\n }\n\n /**\n * Check if line item has source tracking\n */\n hasSource(): boolean {\n return !!this.sourceType && !!this.sourceId;\n }\n\n /**\n * Check if line item has a service period\n */\n hasPeriod(): boolean {\n return !!this.periodStart && !!this.periodEnd;\n }\n\n /**\n * Convert to line item format for SDK accounting provider.\n *\n * `@happyvertical/accounting` takes currency **major** units. Pass the\n * invoice currency to convert (as {@link Invoice.toAccountingInput} does);\n * without it, money stays in this package's integer minor units.\n */\n toAccountingLineItem(currency?: string): AccountingLineItemInput {\n const money = (value: number) =>\n currency ? minorToMajorUnits(value, currency) : value;\n return {\n description: this.description,\n sku: this.sku || undefined,\n quantity: this.quantity,\n unitPrice: money(this.unitPrice),\n discount: this.discount ? money(this.discount) : undefined,\n taxRate: this.taxRate,\n amount: money(this.amount),\n periodStart: this.periodStart || undefined,\n periodEnd: this.periodEnd || undefined,\n };\n }\n}\n\nexport default InvoiceLineItem;\n","/**\n * InvoiceLineItemCollection - Collection manager for InvoiceLineItem objects\n * @packageDocumentation\n */\n\nimport {\n SmrtCollection,\n type SmrtListOptions,\n type SmrtObject,\n type SmrtSelectedRow,\n type SmrtSelectField,\n type SmrtStiReadScope,\n type SmrtWhereClause,\n} from '@happyvertical/smrt-core';\nimport {\n getTenantId,\n queryGlobal,\n queryWithGlobals,\n} from '@happyvertical/smrt-tenancy';\nimport { DOCUMENT_MONEY_READ_REBIND } from '../document-money-internal.js';\nimport { InvoiceLineItem } from '../models/InvoiceLineItem.js';\nimport { withTenantMoneyCollectionRead } from '../tenant-money-barrier.js';\nimport type { AccountingLineItemInput } from '../types/index.js';\n\nexport class InvoiceLineItemCollection extends SmrtCollection<InvoiceLineItem> {\n static readonly _itemClass = InvoiceLineItem;\n\n private async withMoneyRead<T>(operation: () => Promise<T>): Promise<T> {\n const rootDb = this.db;\n const tenantId = getTenantId();\n if (!tenantId) return operation();\n const result = await withTenantMoneyCollectionRead(\n rootDb,\n tenantId,\n (boundDb) => this.withDatabase(boundDb, operation),\n );\n if (result instanceof InvoiceLineItem)\n result.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);\n else if (Array.isArray(result))\n for (const item of result)\n if (item instanceof InvoiceLineItem)\n item.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);\n return result;\n }\n\n override async get(\n filter: string | SmrtWhereClause<InvoiceLineItem>,\n options: Parameters<SmrtCollection<InvoiceLineItem>['get']>[1] = {},\n ): Promise<InvoiceLineItem | null> {\n return this.withMoneyRead(() => super.get(filter, options));\n }\n\n override async list<\n const Select extends readonly SmrtSelectField<InvoiceLineItem>[],\n >(\n options: Omit<SmrtListOptions<InvoiceLineItem>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<Record<string, unknown>[]>;\n override async list<\n const Select extends readonly SmrtSelectField<InvoiceLineItem>[],\n >(\n options: Omit<SmrtListOptions<InvoiceLineItem>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope?: undefined;\n },\n ): Promise<SmrtSelectedRow<InvoiceLineItem, Select>[]>;\n override async list(\n options: Omit<SmrtListOptions<InvoiceLineItem>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<SmrtObject[]>;\n override async list(options?: undefined): Promise<InvoiceLineItem[]>;\n override async list(\n options: Omit<SmrtListOptions<InvoiceLineItem>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope?: undefined;\n },\n ): Promise<InvoiceLineItem[]>;\n override async list(\n options:\n | (Omit<SmrtListOptions<InvoiceLineItem>, 'select'> & {\n select?: undefined;\n })\n | undefined,\n ): Promise<SmrtObject[]>;\n override async list(\n options: SmrtListOptions<InvoiceLineItem> | undefined,\n ): Promise<SmrtObject[] | Record<string, unknown>[]>;\n override async list(\n options: SmrtListOptions<InvoiceLineItem> = {},\n ): Promise<SmrtObject[] | Record<string, unknown>[]> {\n return this.withMoneyRead(() => super.list(options));\n }\n\n /**\n * Find line items by invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Array of line items, sorted by sortOrder\n */\n async findByInvoice(invoiceId: string): Promise<InvoiceLineItem[]> {\n return await this.list({\n where: { invoiceId },\n orderBy: 'sortOrder ASC',\n });\n }\n\n /**\n * Find line items by source\n *\n * @param sourceType - Source type (e.g., 'campaign', 'contract')\n * @param sourceId - Source ID\n * @returns Array of line items\n */\n async findBySource(\n sourceType: string,\n sourceId: string,\n ): Promise<InvoiceLineItem[]> {\n return await this.list({\n where: { sourceType, sourceId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find line items by source type only\n *\n * @param sourceType - Source type (e.g., 'campaign')\n * @returns Array of line items\n */\n async findBySourceType(sourceType: string): Promise<InvoiceLineItem[]> {\n return await this.list({\n where: { sourceType },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Calculate total amount for an invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Total amount\n */\n async getTotalForInvoice(invoiceId: string): Promise<number> {\n const lineItems = await this.findByInvoice(invoiceId);\n return lineItems.reduce((sum, item) => sum + item.amount, 0);\n }\n\n /**\n * Calculate subtotal (before tax) for an invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Subtotal\n */\n async getSubtotalForInvoice(invoiceId: string): Promise<number> {\n const lineItems = await this.findByInvoice(invoiceId);\n return lineItems.reduce((sum, item) => sum + item.getSubtotal(), 0);\n }\n\n /**\n * Calculate tax amount for an invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Tax amount\n */\n async getTaxForInvoice(invoiceId: string): Promise<number> {\n const lineItems = await this.findByInvoice(invoiceId);\n return lineItems.reduce((sum, item) => sum + item.getTaxAmount(), 0);\n }\n\n /**\n * Get the next sort order for an invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Next sort order number\n */\n async getNextSortOrder(invoiceId: string): Promise<number> {\n const lineItems = await this.findByInvoice(invoiceId);\n if (lineItems.length === 0) return 0;\n\n const maxSortOrder = Math.max(...lineItems.map((item) => item.sortOrder));\n return maxSortOrder + 1;\n }\n\n /**\n * Recalculate all line item amounts for an invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Updated line items\n */\n async recalculateAmounts(invoiceId: string): Promise<InvoiceLineItem[]> {\n const lineItems = await this.findByInvoice(invoiceId);\n\n for (const item of lineItems) {\n item.amount = item.calculateAmount();\n }\n\n await Promise.all(lineItems.map((item) => item.save()));\n\n return lineItems;\n }\n\n /**\n * Convert line items to accounting format for SDK sync\n *\n * @param invoiceId - Invoice ID\n * @returns Array of line items in accounting format, money in integer\n * minor units (`Invoice.toAccountingInput()` converts to major units)\n */\n async toAccountingLineItems(\n invoiceId: string,\n ): Promise<AccountingLineItemInput[]> {\n const lineItems = await this.findByInvoice(invoiceId);\n return lineItems.map((item) => item.toAccountingLineItem());\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all invoice line items belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of invoice line items for the tenant\n */\n async findByTenant(tenantId: string): Promise<InvoiceLineItem[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global invoice line items (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global invoice line items\n */\n async findGlobal(): Promise<InvoiceLineItem[]> {\n return queryGlobal<InvoiceLineItem>(this);\n }\n\n /**\n * Find invoice line items for a tenant including global line items.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID\n * @returns Array of tenant-specific and global invoice line items\n */\n async findWithGlobals(tenantId: string): Promise<InvoiceLineItem[]> {\n return queryWithGlobals<InvoiceLineItem>(\n this,\n tenantId,\n 'InvoiceLineItem.findWithGlobals',\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -75,7 +75,7 @@ var TaxCode = class extends SmrtObject {
|
|
|
75
75
|
if (component.compound) throw new MoneyValidationError("COMPOUND_UNSUPPORTED", "Compound tax components are reserved for a future release.");
|
|
76
76
|
}
|
|
77
77
|
return this.withTransaction(async () => {
|
|
78
|
-
const { TaxComponentCollection } = await import("./TaxComponentCollection-
|
|
78
|
+
const { TaxComponentCollection } = await import("./TaxComponentCollection-3x24xSNh.js").then((n) => n.n);
|
|
79
79
|
const components = await TaxComponentCollection.create({ db: this.db });
|
|
80
80
|
for (const part of parts) {
|
|
81
81
|
const component = await components.get({ id: part.taxComponentId }, { cache: false });
|
|
@@ -146,4 +146,4 @@ var TaxCodeCollection = class extends SmrtCollection {
|
|
|
146
146
|
//#endregion
|
|
147
147
|
export { TaxCodeCollection_exports as n, TaxCode as r, TaxCodeCollection as t };
|
|
148
148
|
|
|
149
|
-
//# sourceMappingURL=TaxCodeCollection-
|
|
149
|
+
//# sourceMappingURL=TaxCodeCollection-DF2eaJed.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaxCodeCollection-
|
|
1
|
+
{"version":3,"file":"TaxCodeCollection-DF2eaJed.js","names":[],"sources":["../../src/models/TaxCode.ts","../../src/collections/TaxCodeCollection.ts"],"sourcesContent":["import { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { MoneyValidationError } from '../money.js';\nimport type { TaxCodeOptions, TaxCodePart } from '../types/index.js';\n\nexport type TaxCodeKind = 'taxable' | 'zero-rated' | 'exempt' | 'out-of-scope';\nexport type TaxCodeAppliesTo = 'sales' | 'purchases' | 'both';\n\n/** A reusable tax code whose ordered component list is safe to edit. */\n@TenantScoped({ mode: 'required' })\n@smrt({\n conflictColumns: ['tenant_id', 'code'],\n api: {\n include: ['list', 'get', 'create', 'update'],\n writable: ['code', 'name', 'kind', 'appliesTo', 'components', 'active'],\n },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class TaxCode extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-commerce\";\n @tenantId()\n tenantId = '';\n\n @field({ description: 'A stable short code, for example gst-pst-bc.' })\n code = '';\n\n @field({ description: 'The name people see for this tax code.' })\n name = '';\n\n @field({ description: 'How the tax code is reported.' })\n kind: TaxCodeKind = 'taxable';\n\n @field({\n description: 'Whether the code applies to sales, purchases or both.',\n })\n appliesTo: TaxCodeAppliesTo = 'both';\n\n /** Ordered component IDs; compound is reserved and deliberately unsupported. */\n /** Stored JSON is guarded through the typed accessor below. */\n componentsJson = '[]';\n\n @field({\n description: 'The ordered tax components in this code.',\n transient: true,\n })\n get components(): TaxCodePart[] {\n try {\n const value: unknown = JSON.parse(this.componentsJson);\n if (!Array.isArray(value)) return [];\n return value.filter(\n (part): part is TaxCodePart =>\n !!part &&\n typeof part === 'object' &&\n typeof (part as TaxCodePart).taxComponentId === 'string' &&\n typeof (part as TaxCodePart).compound === 'boolean',\n );\n } catch {\n return [];\n }\n }\n\n set components(value: TaxCodePart[] | string) {\n this.componentsJson =\n typeof value === 'string' ? value : JSON.stringify(value);\n }\n\n @field({\n description: 'Whether users may select this code for new documents.',\n })\n active = true;\n\n constructor(options: TaxCodeOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId ?? '';\n if (options.code !== undefined) this.code = options.code;\n if (options.name !== undefined) this.name = options.name;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.appliesTo !== undefined) this.appliesTo = options.appliesTo;\n if (options.components !== undefined)\n this.components = structuredClone(options.components);\n if (options.active !== undefined) this.active = options.active;\n }\n\n override async save(): Promise<this> {\n if (!this.code.trim() || !this.name.trim())\n throw new Error('Tax code requires a nonempty code and name');\n if (\n !['taxable', 'zero-rated', 'exempt', 'out-of-scope'].includes(this.kind)\n )\n throw new Error('Tax code kind is invalid');\n if (!['sales', 'purchases', 'both'].includes(this.appliesTo))\n throw new Error('Tax code appliesTo is invalid');\n const parts = this.parsedComponents();\n if (!parts)\n throw new Error('Tax code components must be a valid JSON array');\n const nonTaxable = this.kind !== 'taxable';\n if (nonTaxable && parts.length) {\n throw new MoneyValidationError(\n 'TAX_CODE_NON_TAXABLE_COMPONENTS',\n `${this.kind} tax codes cannot have components.`,\n );\n }\n if (!nonTaxable && !parts.length) {\n throw new MoneyValidationError(\n 'TAX_CODE_TAXABLE_EMPTY',\n 'Taxable tax codes require at least one component.',\n );\n }\n const seen = new Set<string>();\n for (const component of parts) {\n if (seen.has(component.taxComponentId)) {\n throw new MoneyValidationError(\n 'TAX_CODE_DUPLICATE_COMPONENT',\n 'A tax code cannot contain the same component twice.',\n );\n }\n seen.add(component.taxComponentId);\n if (component.compound) {\n throw new MoneyValidationError(\n 'COMPOUND_UNSUPPORTED',\n 'Compound tax components are reserved for a future release.',\n );\n }\n }\n return this.withTransaction(async () => {\n const { TaxComponentCollection } = await import(\n '../collections/TaxComponentCollection.js'\n );\n const components = await TaxComponentCollection.create({ db: this.db });\n for (const part of parts) {\n const component = await components.get(\n { id: part.taxComponentId },\n { cache: false },\n );\n if (!component) {\n throw new Error('Tax code components must belong to the same tenant');\n }\n if (!component.updated_at)\n throw new Error('Tax code component is missing its revision');\n await component.claimRevision(component.updated_at);\n if (!this.tenantId) this.tenantId = component.tenantId;\n if (component.tenantId !== this.tenantId)\n throw new Error('Tax code components must belong to the same tenant');\n }\n return (await super.save()) as this;\n });\n }\n\n private parsedComponents(): TaxCodePart[] | null {\n try {\n const value: unknown = JSON.parse(this.componentsJson);\n if (!Array.isArray(value)) return null;\n if (\n value.some(\n (part) =>\n !part ||\n typeof part !== 'object' ||\n typeof (part as TaxCodePart).taxComponentId !== 'string' ||\n !(part as TaxCodePart).taxComponentId.trim() ||\n typeof (part as TaxCodePart).compound !== 'boolean',\n )\n )\n return null;\n return value as TaxCodePart[];\n } catch {\n return null;\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TaxCode } from '../models/TaxCode.js';\n\nexport class TaxCodeCollection extends SmrtCollection<TaxCode> {\n static readonly _itemClass = TaxCode;\n\n async findByCode(code: string): Promise<TaxCode | null> {\n return (await this.list({ where: { code }, limit: 1 }))[0] ?? null;\n }\n}\n"],"mappings":"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"}
|
package/dist/chunks/{TaxComponentCollection-Cs2uXwEl.js → TaxComponentCollection-3x24xSNh.js}
RENAMED
|
@@ -76,8 +76,8 @@ var TaxComponent = class extends SmrtObject {
|
|
|
76
76
|
if (!this.id) return false;
|
|
77
77
|
if (!persisted.updated_at) throw new Error("Referenced tax component is missing its revision");
|
|
78
78
|
await persisted.claimRevision(persisted.updated_at);
|
|
79
|
-
const { TaxCodeCollection } = await import("./TaxCodeCollection-
|
|
80
|
-
const { DocumentTaxCollection } = await import("./DocumentTaxCollection-
|
|
79
|
+
const { TaxCodeCollection } = await import("./TaxCodeCollection-DF2eaJed.js").then((n) => n.n);
|
|
80
|
+
const { DocumentTaxCollection } = await import("./DocumentTaxCollection-KOkaQT2M.js").then((n) => n.n);
|
|
81
81
|
if ((await (await TaxCodeCollection.create({ db: this.db })).list({ where: { tenantId: this.tenantId } })).some((code) => code.components.some((component) => component.taxComponentId === this.id))) return true;
|
|
82
82
|
return (await (await DocumentTaxCollection.create({ db: this.db })).list({
|
|
83
83
|
where: {
|
|
@@ -126,4 +126,4 @@ var TaxComponentCollection = class extends SmrtCollection {
|
|
|
126
126
|
//#endregion
|
|
127
127
|
export { TaxComponentCollection_exports as n, TaxComponent as r, TaxComponentCollection as t };
|
|
128
128
|
|
|
129
|
-
//# sourceMappingURL=TaxComponentCollection-
|
|
129
|
+
//# sourceMappingURL=TaxComponentCollection-3x24xSNh.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaxComponentCollection-
|
|
1
|
+
{"version":3,"file":"TaxComponentCollection-3x24xSNh.js","names":[],"sources":["../../src/models/TaxComponent.ts","../../src/collections/TaxComponentCollection.ts"],"sourcesContent":["import {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { AccountCollection } from '@happyvertical/smrt-ledgers';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { MoneyValidationError } from '../money.js';\nimport type { TaxComponentOptions } from '../types/index.js';\n\nconst PERCENT = /^(\\d+)(?:\\.(\\d{1,6}))?$/;\n\nfunction isPercent(value: string): boolean {\n const match = PERCENT.exec(value);\n if (!match) return false;\n const scaled =\n BigInt(match[1]) * 1_000_000n + BigInt((match[2] ?? '').padEnd(6, '0'));\n return scaled <= 100_000_000n;\n}\n\nfunction canonicalPercent(value: string): string {\n const [whole, fraction = ''] = String(value).split('.');\n const normalizedWhole = whole.replace(/^0+(?=\\d)/, '') || '0';\n const normalizedFraction = fraction.replace(/0+$/, '');\n return normalizedFraction\n ? `${normalizedWhole}.${normalizedFraction}`\n : normalizedWhole;\n}\n\n/**\n * One reusable tax rate component. Codes may combine components, but the\n * component's percent is immutable after it has been saved: archive it and\n * repoint editable codes to a replacement rate instead of rewriting history.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n api: {\n include: ['list', 'get', 'create', 'update'],\n writable: [\n 'name',\n 'percent',\n 'liabilityAccountId',\n 'recoverable',\n 'active',\n ],\n },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class TaxComponent extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-commerce\";\n @tenantId()\n tenantId = '';\n\n @field({ description: 'The name of this tax component, for example GST.' })\n name = '';\n\n /** Percent, not a fraction: 7.125 means 7.125%. Stored as DECIMAL. */\n @field({\n type: 'decimal',\n description: 'The tax percentage, for example 7.125 for 7.125 percent.',\n })\n percent = '0.0';\n\n @crossPackageRef('@happyvertical/smrt-ledgers:Account', {\n description: 'The liability account that receives this tax component.',\n })\n liabilityAccountId = '';\n\n @field({\n description:\n 'Whether purchases using this component may recover the full input tax. Partial recovery is not supported.',\n })\n recoverable = false;\n\n @field({ description: 'Whether new tax codes and overrides may select it.' })\n active = true;\n\n constructor(options: TaxComponentOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId ?? '';\n if (options.name !== undefined) this.name = options.name;\n if (options.percent !== undefined) this.percent = options.percent;\n if (options.liabilityAccountId !== undefined)\n this.liabilityAccountId = options.liabilityAccountId;\n if (options.recoverable !== undefined)\n this.recoverable = options.recoverable;\n if (options.active !== undefined) this.active = options.active;\n }\n\n override async initialize(): Promise<this> {\n await super.initialize();\n this.percent = canonicalPercent(String(this.percent));\n return this;\n }\n\n override async save(): Promise<this> {\n if (!this.name.trim())\n throw new Error('Tax component requires a nonempty name');\n if (!isPercent(this.percent)) {\n throw new MoneyValidationError(\n 'PERCENT_OUT_OF_RANGE',\n 'Tax component percent must be between 0 and 100 with at most six decimal places.',\n );\n }\n this.percent = canonicalPercent(this.percent);\n return this.withTransaction(async () => {\n const { TaxComponentCollection } = await import(\n '../collections/TaxComponentCollection.js'\n );\n const components = await TaxComponentCollection.create({ db: this.db });\n const persisted = this.id\n ? await components.get({ id: this.id }, { cache: false })\n : null;\n if (persisted && canonicalPercent(persisted.percent) !== this.percent) {\n const referenced = await this.isReferenced(persisted);\n // claimRevision advanced the row's version; the pending save must use\n // that version rather than its stale caller snapshot.\n this.updated_at = persisted.updated_at;\n if (referenced) {\n throw new MoneyValidationError(\n 'PERCENT_OUT_OF_RANGE',\n 'A referenced tax component rate is immutable; archive it and repoint its tax codes.',\n );\n }\n }\n if (this.liabilityAccountId) {\n const accounts = await AccountCollection.create({ db: this.db });\n const account = await accounts.get(\n { id: this.liabilityAccountId },\n { cache: false },\n );\n if (!account || account.tenantId !== this.tenantId) {\n throw new Error(\n 'Tax component liability account must belong to its tenant',\n );\n }\n }\n return (await super.save()) as this;\n });\n }\n\n private async isReferenced(persisted: TaxComponent): Promise<boolean> {\n if (!this.id) return false;\n if (!persisted.updated_at)\n throw new Error('Referenced tax component is missing its revision');\n // Claim before inspecting references. TaxCode.save() claims the same row\n // before attaching it, so a concurrent rate edit must reload and retry.\n await persisted.claimRevision(persisted.updated_at);\n const { TaxCodeCollection } = await import(\n '../collections/TaxCodeCollection.js'\n );\n const { DocumentTaxCollection } = await import(\n '../collections/DocumentTaxCollection.js'\n );\n const codes = await TaxCodeCollection.create({ db: this.db });\n const usedByCode = (\n await codes.list({ where: { tenantId: this.tenantId } })\n ).some((code) =>\n code.components.some((component) => component.taxComponentId === this.id),\n );\n if (usedByCode) return true;\n const documentTaxes = await DocumentTaxCollection.create({ db: this.db });\n return (\n (\n await documentTaxes.list({\n where: { tenantId: this.tenantId, taxComponentId: this.id },\n limit: 1,\n })\n ).length > 0\n );\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TaxComponent } from '../models/TaxComponent.js';\n\nexport class TaxComponentCollection extends SmrtCollection<TaxComponent> {\n static readonly _itemClass = TaxComponent;\n}\n"],"mappings":"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"}
|
|
@@ -139,8 +139,8 @@ var TaxSettings = class extends SmrtObject {
|
|
|
139
139
|
if (!registrations) throw new Error("Tax registrations must be a valid JSON array");
|
|
140
140
|
if (registrations.some((entry) => !entry.taxComponentId || !entry.number.trim() || !entry.label.trim())) throw new Error("Tax registrations require component, number and label");
|
|
141
141
|
const persist = async () => {
|
|
142
|
-
const { TaxCodeCollection } = await import("./TaxCodeCollection-
|
|
143
|
-
const { TaxComponentCollection } = await import("./TaxComponentCollection-
|
|
142
|
+
const { TaxCodeCollection } = await import("./TaxCodeCollection-DF2eaJed.js").then((n) => n.n);
|
|
143
|
+
const { TaxComponentCollection } = await import("./TaxComponentCollection-3x24xSNh.js").then((n) => n.n);
|
|
144
144
|
const codes = await TaxCodeCollection.create({ db: this.db });
|
|
145
145
|
const components = await TaxComponentCollection.create({ db: this.db });
|
|
146
146
|
if (!allowMoneyModelWrite) {
|
|
@@ -275,7 +275,7 @@ var TaxSettingsCollection = class TaxSettingsCollection extends SmrtCollection {
|
|
|
275
275
|
async beginMoneyMigration(tenantId, requestedMigrationId) {
|
|
276
276
|
if (requestedMigrationId !== void 0 && !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(requestedMigrationId)) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Migration owner identity must be a UUID");
|
|
277
277
|
return withDocumentMoneyTransaction(this.db, async (tx) => {
|
|
278
|
-
const { lockTenantMoneyMigrationClaim } = await import("./tenant-money-barrier-
|
|
278
|
+
const { lockTenantMoneyMigrationClaim } = await import("./tenant-money-barrier-y6EAa6vY.js").then((n) => n.r);
|
|
279
279
|
const current = await lockTenantMoneyMigrationClaim(tx, tenantId);
|
|
280
280
|
const migrationId = requestedMigrationId ?? crypto.randomUUID();
|
|
281
281
|
current.moneyModel = "migrating";
|
|
@@ -286,7 +286,7 @@ var TaxSettingsCollection = class TaxSettingsCollection extends SmrtCollection {
|
|
|
286
286
|
}
|
|
287
287
|
async activateMoneyV2(tenantId, migrationId) {
|
|
288
288
|
await withDocumentMoneyTransaction(this.db, async (tx) => {
|
|
289
|
-
const { lockTenantMoneyMigrationOwner } = await import("./tenant-money-barrier-
|
|
289
|
+
const { lockTenantMoneyMigrationOwner } = await import("./tenant-money-barrier-y6EAa6vY.js").then((n) => n.r);
|
|
290
290
|
const current = await lockTenantMoneyMigrationOwner(tx, tenantId, migrationId);
|
|
291
291
|
current.moneyModel = "v2";
|
|
292
292
|
current.moneyMigrationId = "";
|
|
@@ -295,7 +295,7 @@ var TaxSettingsCollection = class TaxSettingsCollection extends SmrtCollection {
|
|
|
295
295
|
}
|
|
296
296
|
async restoreLegacyMoneyModel(tenantId, migrationId) {
|
|
297
297
|
await withDocumentMoneyTransaction(this.db, async (tx) => {
|
|
298
|
-
const { lockTenantMoneyMigrationOwner } = await import("./tenant-money-barrier-
|
|
298
|
+
const { lockTenantMoneyMigrationOwner } = await import("./tenant-money-barrier-y6EAa6vY.js").then((n) => n.r);
|
|
299
299
|
const current = await lockTenantMoneyMigrationOwner(tx, tenantId, migrationId);
|
|
300
300
|
current.moneyModel = "legacy";
|
|
301
301
|
current.moneyMigrationId = "";
|
|
@@ -316,4 +316,4 @@ var TaxSettingsCollection = class TaxSettingsCollection extends SmrtCollection {
|
|
|
316
316
|
//#endregion
|
|
317
317
|
export { DOCUMENT_MONEY_READ_REBIND as a, lockDocumentMoneyParents as c, TaxSettings as i, withDocumentMoneyObjectTransaction as l, createOrLoadTaxSeed as n, DOCUMENT_MONEY_WRITE as o, deterministicTaxId as r, MONEY_MIGRATION_WRITE as s, TaxSettingsCollection as t, withDocumentMoneyTransaction as u };
|
|
318
318
|
|
|
319
|
-
//# sourceMappingURL=TaxSettingsCollection-
|
|
319
|
+
//# sourceMappingURL=TaxSettingsCollection-DYYF-In-.js.map
|