@happyvertical/smrt-commerce 0.35.1 → 0.35.3

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/__smrt-register__.ts","../src/types/index.ts","../src/models/Customer.ts","../src/models/Vendor.ts","../src/models/Contract.ts","../src/collections/ContractCollection.ts","../src/models/ContractLineItem.ts","../src/collections/ContractLineItemCollection.ts","../src/collections/CustomerCollection.ts","../src/models/Fulfillment.ts","../src/collections/FulfillmentCollection.ts","../src/models/FulfillmentLineItem.ts","../src/collections/FulfillmentLineItemCollection.ts","../src/models/Invoice.ts","../src/collections/InvoiceCollection.ts","../src/models/InvoiceLineItem.ts","../src/collections/InvoiceLineItemCollection.ts","../src/models/PaymentAllocation.ts","../src/collections/PaymentAllocationCollection.ts","../src/models/Payment.ts","../src/collections/PaymentCollection.ts","../src/models/PaymentIntent.ts","../src/collections/PaymentIntentCollection.ts","../src/models/Payout.ts","../src/collections/PayoutCollection.ts","../src/collections/VendorCollection.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// `new URL('./manifest.json', import.meta.url)` resolves at runtime to the\n// manifest sitting next to this module's compiled output. Vite warns at build\n// time that it cannot pre-resolve the URL; that is the intended behavior —\n// the URL must resolve to dist/manifest.json at runtime, not be inlined.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Commerce package type definitions\n * @packageDocumentation\n */\n\n// ============================================================================\n// Customer/Vendor Types\n// ============================================================================\n\nexport enum CustomerStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n SUSPENDED = 'suspended',\n}\n\n/**\n * Customer classification — drives channel access, pricing tier, and credit terms.\n *\n * - DTC: direct-to-consumer (end shopper). Pays via storefront checkout.\n * - WHOLESALE: B2B buyer with negotiated terms (NET-30, line sheets, etc.).\n * - RETAIL: in-store walk-in / point-of-sale customer.\n */\nexport enum CustomerType {\n DTC = 'dtc',\n WHOLESALE = 'wholesale',\n RETAIL = 'retail',\n}\n\nexport enum VendorStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n SUSPENDED = 'suspended',\n}\n\n// ============================================================================\n// Contract Types\n// ============================================================================\n\nexport enum ContractType {\n ESTIMATE = 'estimate',\n ORDER = 'order',\n LEASE = 'lease',\n AGREEMENT = 'agreement',\n PURCHASE_ORDER = 'purchase_order',\n WHOLESALE_ORDER = 'wholesale_order',\n PRODUCTION_ORDER = 'production_order',\n CART = 'cart',\n /**\n * Industry-neutral licensing primitive. The `LicenseSale` STI subtype\n * carries an immutable rights snapshot, a licensee email, optional\n * legal-entity / jurisdiction fields, and a signed-PDF reference.\n * Any application that sells rights for a fee (stock media,\n * music licensing, code-asset marketplace, license keys, etc.)\n * uses this subtype.\n */\n LICENSE_SALE = 'license_sale',\n}\n\nexport enum ContractStatus {\n DRAFT = 'draft',\n SENT = 'sent',\n ACCEPTED = 'accepted',\n DECLINED = 'declined',\n COMPLETED = 'completed',\n CANCELLED = 'cancelled',\n}\n\n// ============================================================================\n// Fulfillment Types\n// ============================================================================\n\nexport enum FulfillmentType {\n SHIPMENT = 'shipment',\n DELIVERY = 'delivery',\n PICKUP = 'pickup',\n DIGITAL = 'digital',\n SERVICE = 'service',\n}\n\nexport enum FulfillmentStatus {\n PENDING = 'pending',\n PROCESSING = 'processing',\n SHIPPED = 'shipped',\n DELIVERED = 'delivered',\n CANCELLED = 'cancelled',\n}\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\nexport enum PaymentMethod {\n CASH = 'cash',\n CHECK = 'check',\n CREDIT_CARD = 'credit_card',\n BANK_TRANSFER = 'bank_transfer',\n CRYPTO = 'crypto',\n OTHER = 'other',\n}\n\nexport enum PaymentStatus {\n PENDING = 'pending',\n COMPLETED = 'completed',\n FAILED = 'failed',\n REFUNDED = 'refunded',\n CANCELLED = 'cancelled',\n}\n\n// ============================================================================\n// Payout Types\n// ============================================================================\n\n/**\n * Status machine for {@link Payout}.\n *\n * `pending → sent → confirmed → failed`. `failed` is terminal but the\n * model exposes a dedicated `resetFromFailed()` so an operator can\n * deliberately requeue a payout after fixing the underlying problem;\n * the reset moves the row back to `pending` and clears the failure\n * metadata.\n *\n * Skipping intermediate states is rejected — a `pending` payout cannot\n * jump straight to `confirmed`, and a `sent` payout cannot regress to\n * `pending` without going through `resetFromFailed()` first.\n */\nexport enum PayoutStatus {\n PENDING = 'pending',\n SENT = 'sent',\n CONFIRMED = 'confirmed',\n FAILED = 'failed',\n}\n\n// ============================================================================\n// PaymentIntent Types\n// ============================================================================\n\n/**\n * Status machine for {@link PaymentIntent}.\n *\n * `awaiting_payment → paid → (issued | retired)`, with `expired` and\n * `cancelled` as alternate terminal states reachable from\n * `awaiting_payment`.\n *\n * - `AWAITING_PAYMENT` — open quote, accepting any of the listed\n * payment options.\n * - `PAID` — one option was satisfied by an incoming payment. The\n * other options are implicitly retired; later inbound funds to a\n * retired option must be flagged for refund by the consumer.\n * - `ISSUED` — downstream rights / contract / fulfillment have been\n * created and linked. Terminal happy-path state.\n * - `RETIRED` — paid but the satisfaction was reversed (refund,\n * chargeback, etc.). Terminal.\n * - `EXPIRED` — the USD-price-lock window passed without payment.\n * Terminal.\n * - `CANCELLED` — the buyer or the system explicitly cancelled the\n * intent before it was paid. Terminal.\n */\nexport enum PaymentIntentStatus {\n AWAITING_PAYMENT = 'awaiting_payment',\n PAID = 'paid',\n ISSUED = 'issued',\n RETIRED = 'retired',\n EXPIRED = 'expired',\n CANCELLED = 'cancelled',\n}\n\n/**\n * One way for a {@link PaymentIntent} to be satisfied. The intent lists\n * an array of these; satisfying any one of them transitions the intent\n * to `PAID` and implicitly retires the rest.\n *\n * Industry-neutral: a marketplace that accepts USDC-on-Base and BTC\n * lists two options; a SaaS billing flow that accepts Stripe and\n * PayPal lists two; a kiosk that accepts only one rail lists one.\n */\nexport interface PaymentOption {\n /**\n * Stable id of the `PaymentBackend` adapter that fulfills this\n * option — must match a `backendId` the consumer's payment-routing\n * layer recognises. Examples: `base-usdc`, `solana-usdc`, `btc`,\n * `stripe`, `paypal`.\n */\n backendId: string;\n /**\n * Payout-rail-qualified currency code, e.g. `USDC-base`, `BTC`,\n * `USD-stripe`. The same convention used by\n * `Vendor.payoutAddresses` and `Payment.nativeCurrency`.\n */\n currency: string;\n /**\n * Optional chain identifier for blockchain-backed options (`base`,\n * `ethereum`, `solana`, ...). Pure-fiat options leave this empty.\n */\n chain?: string;\n /**\n * Destination address / account id the buyer should send funds to\n * for this option (EVM address, BTC address, Stripe payment-intent\n * id, etc.).\n */\n payTo: string;\n /**\n * Amount denominated in `currency`. Stored at decimal precision —\n * a USDC option might be `199.0`, a BTC option `0.00713`.\n */\n nativeAmount: number;\n /**\n * Optional on-chain memo / payment reference (Solana memo, BTC OP_RETURN\n * tag, Stripe metadata key). Some backends use this to disambiguate\n * which intent an inbound payment satisfies.\n */\n memo?: string;\n /**\n * Whether this option supports the x402 HTTP-402 payment-required\n * flow. Consumers that build agent-driven flows use this flag to\n * filter the offered options.\n */\n x402Capable?: boolean;\n /**\n * Optional per-option expiry — when a single rail's quote becomes\n * stale earlier than the intent-wide price-lock window (e.g. a\n * volatile-currency option that re-quotes more aggressively). ISO\n * 8601 string. Empty / missing means \"inherit the intent's\n * `priceLockExpiresAt`\".\n */\n expiresAt?: string;\n}\n\n// ============================================================================\n// Invoice Types\n// ============================================================================\n\nexport enum InvoiceStatus {\n DRAFT = 'draft',\n SENT = 'sent',\n VIEWED = 'viewed',\n PARTIAL = 'partial',\n PAID = 'paid',\n OVERDUE = 'overdue',\n CANCELLED = 'cancelled',\n WRITTEN_OFF = 'written_off',\n}\n\n// ============================================================================\n// Common Interfaces\n// ============================================================================\n\n/**\n * Address structure used for billing and shipping\n */\nexport interface Address {\n street1?: string;\n street2?: string;\n city?: string;\n state?: string;\n postalCode?: string;\n country?: string;\n}\n\n/**\n * Options for recording a payment with ledger integration\n */\nexport interface RecordPaymentOptions {\n /** The ledger to record the journal entry in */\n ledgerId: string;\n /** Account ID for receivables (credit side) */\n receivablesAccountId: string;\n /** Account ID for cash/bank (debit side) */\n cashAccountId: string;\n}\n\n/**\n * Options for recognizing revenue on an invoice\n */\nexport interface RecognizeRevenueOptions {\n /** Account ID for accounts receivable (debit side) */\n arAccountId: string;\n /** Account ID for revenue (credit side) */\n revenueAccountId: string;\n /** Account ID for tax payable (credit side, optional) */\n taxAccountId?: string;\n}\n","/**\n * Customer model - links to Profile with customer-specific data\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { type Address, CustomerStatus, CustomerType } from '../types/index.js';\n\n/**\n * Customer represents the customer role for a Profile.\n *\n * One Profile can have multiple Customer records (e.g., different business contexts).\n * Customer stores customer-specific data like credit limits and payment terms.\n *\n * @example\n * ```typescript\n * const customer = await customers.create({\n * profileId: 'profile-uuid',\n * creditLimit: 10000,\n * paymentTerms: 'Net 30'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Customer extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global customers\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Reference to smrt-profiles Profile\n * Plain string for cross-package reference\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId: string = '';\n\n /**\n * Maximum credit extended to this customer\n */\n creditLimit: number = 0.0;\n\n /**\n * Payment terms (e.g., \"Net 30\", \"Due on receipt\", \"2/10 Net 30\")\n */\n paymentTerms: string = '';\n\n /**\n * Whether customer is exempt from tax\n */\n taxExempt: boolean = false;\n\n /**\n * Tax identification number\n *\n * Sensitive (#1540): PII excluded from generated API/MCP responses and\n * rejected as a `where` filter key.\n */\n @field({ sensitive: true })\n taxId: string = '';\n\n /**\n * Default shipping address\n */\n defaultShippingAddress: Address = {};\n\n /**\n * Default billing address\n */\n defaultBillingAddress: Address = {};\n\n /**\n * Customer status\n */\n status: CustomerStatus = CustomerStatus.ACTIVE;\n\n /**\n * Customer classification — DTC, wholesale, or retail.\n *\n * Drives channel access (wholesale portal vs. storefront vs. POS), default\n * price tier, and credit terms. Defaults to DTC for backwards compatibility.\n */\n customerType: CustomerType = CustomerType.DTC;\n\n /**\n * Internal notes about this customer\n */\n notes: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.creditLimit !== undefined)\n this.creditLimit = options.creditLimit;\n if (options.paymentTerms !== undefined)\n this.paymentTerms = options.paymentTerms;\n if (options.taxExempt !== undefined) this.taxExempt = options.taxExempt;\n if (options.taxId !== undefined) this.taxId = options.taxId;\n if (options.defaultShippingAddress !== undefined)\n this.defaultShippingAddress = options.defaultShippingAddress;\n if (options.defaultBillingAddress !== undefined)\n this.defaultBillingAddress = options.defaultBillingAddress;\n if (options.status !== undefined) this.status = options.status;\n // Treat `null` the same as `undefined` here. Customers loaded from\n // rows written before the `customer_type` column existed will\n // hydrate with the field as SQL `NULL`; without this guard the\n // SmrtObject loader would overwrite the `CustomerType.DTC`\n // initializer with `null`, breaking the documented\n // backwards-compatible default. Explicit string values (including\n // any future custom flavour) still take precedence.\n if (options.customerType !== undefined && options.customerType !== null) {\n this.customerType = options.customerType;\n }\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Convenience predicate: is this a wholesale customer?\n */\n isWholesale(): boolean {\n return this.customerType === CustomerType.WHOLESALE;\n }\n\n /**\n * Check if customer is active\n */\n isActive(): boolean {\n return this.status === CustomerStatus.ACTIVE;\n }\n\n /**\n * Check if customer has available credit\n */\n hasAvailableCredit(amount: number): boolean {\n // In a full implementation, this would check against outstanding balances\n return this.creditLimit >= amount;\n }\n\n /**\n * Get the profile ID for external lookup.\n *\n * To get the actual Profile object, use the ProfileCollection from smrt-profiles:\n * ```typescript\n * const profiles = await ProfileCollection.create(options);\n * const profile = await profiles.get({ id: customer.profileId });\n * ```\n *\n * @returns The profile ID or empty string if not set\n */\n getProfileId(): string {\n return this.profileId;\n }\n}\n\nexport default Customer;\n","/**\n * Vendor model - links to Profile with vendor-specific data\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { VendorStatus } from '../types/index.js';\n\n/**\n * Vendor represents the vendor/supplier role for a Profile.\n *\n * One Profile can have both Customer and Vendor records - e.g., a company\n * you both buy from and sell to.\n *\n * @example\n * ```typescript\n * const vendor = await vendors.create({\n * profileId: 'profile-uuid',\n * leadTimeDays: 14,\n * minimumOrderAmount: 500,\n * paymentTerms: 'Net 60'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Vendor extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global vendors\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Reference to smrt-profiles Profile\n * Plain string for cross-package reference\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId: string = '';\n\n /**\n * Typical lead time in days for orders\n */\n leadTimeDays: number = 0;\n\n /**\n * Minimum order amount required\n */\n minimumOrderAmount: number = 0.0;\n\n /**\n * Payment terms for this vendor (e.g., \"Net 60\")\n */\n paymentTerms: string = '';\n\n /**\n * Default currency for transactions with this vendor\n */\n currency: string = 'USD';\n\n /**\n * Default contact email for orders\n */\n defaultContactEmail: string = '';\n\n /**\n * Default contact phone for orders\n */\n defaultContactPhone: string = '';\n\n /**\n * Vendor status\n */\n status: VendorStatus = VendorStatus.ACTIVE;\n\n /**\n * Internal notes about this vendor\n */\n notes: string = '';\n\n /**\n * Per-currency payout destinations for this vendor.\n *\n * Stored as a JSON column. Keys are payout-rail-qualified currency codes —\n * the code must encode both the asset and the rail (e.g. `USDC-base`,\n * `USDC-solana`, `BTC`, `USD-stripe`, `USD-wire`). Values are whatever the\n * receiving rail needs as a destination (EVM address, BTC address, Stripe\n * Connect account id, IBAN, etc.).\n *\n * MVP shape: a flat map on the vendor row. A vendor with no entry for a\n * given currency cannot be paid in that currency — filtering is the\n * consumer's responsibility (the upstream `PaymentIntent` builder filters\n * `paymentOptions` at quote time against this map).\n *\n * Use {@link getPayoutAddress} for a `Map.get`-style lookup that returns\n * `undefined` for missing entries; direct property access is also fine.\n *\n * Sensitive (#1540): payout destinations are excluded from generated API/MCP\n * responses and rejected as a `where` filter key.\n */\n @field({ sensitive: true })\n payoutAddresses: Record<string, string> = {};\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.leadTimeDays !== undefined)\n this.leadTimeDays = options.leadTimeDays;\n if (options.minimumOrderAmount !== undefined)\n this.minimumOrderAmount = options.minimumOrderAmount;\n if (options.paymentTerms !== undefined)\n this.paymentTerms = options.paymentTerms;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.defaultContactEmail !== undefined)\n this.defaultContactEmail = options.defaultContactEmail;\n if (options.defaultContactPhone !== undefined)\n this.defaultContactPhone = options.defaultContactPhone;\n if (options.status !== undefined) this.status = options.status;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.payoutAddresses !== undefined) {\n this.payoutAddresses = Vendor.normalizePayoutAddresses(\n options.payoutAddresses,\n );\n }\n }\n\n /**\n * The framework's {@link SmrtObject.initialize} re-applies `options` keys on\n * top of constructor-set values so option data always wins over field\n * initializers. That means our constructor's normalization runs *before*\n * `initializePropertiesFromOptions` overwrites `payoutAddresses` with the\n * raw cloned value. Re-normalize once super has finished so consumers\n * who pass a string, a partially-typed map, or `null` end up with a\n * clean `Record<string, string>` before any save / read happens.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.payoutAddresses = Vendor.normalizePayoutAddresses(\n this.payoutAddresses as unknown,\n );\n return this;\n }\n\n /**\n * Save with a final normalization pass so direct field assignments\n * (`vendor.payoutAddresses = somethingFunny`) can't smuggle non-string\n * values into the persisted row.\n */\n override async save(): Promise<this> {\n this.payoutAddresses = Vendor.normalizePayoutAddresses(\n this.payoutAddresses as unknown,\n );\n return super.save() as Promise<this>;\n }\n\n /**\n * Check if vendor is active\n */\n isActive(): boolean {\n return this.status === VendorStatus.ACTIVE;\n }\n\n /**\n * Check if order meets minimum requirements\n */\n meetsMinimumOrder(amount: number): boolean {\n return amount >= this.minimumOrderAmount;\n }\n\n /**\n * Get the profile ID for external lookup.\n *\n * To get the actual Profile object, use the ProfileCollection from smrt-profiles:\n * ```typescript\n * const profiles = await ProfileCollection.create(options);\n * const profile = await profiles.get({ id: vendor.profileId });\n * ```\n *\n * @returns The profile ID or empty string if not set\n */\n getProfileId(): string {\n return this.profileId;\n }\n\n /**\n * Look up the payout destination for a single currency. Returns\n * `undefined` if the vendor has no entry for that currency — caller's\n * job to decide whether that means \"skip the option\" or \"raise a\n * configuration error\". The lookup is case-sensitive; the contract is\n * that the currency code passed in matches exactly what was stored\n * (`USDC-base`, not `usdc-base`).\n */\n getPayoutAddress(currency: string): string | undefined {\n const map = this.payoutAddresses;\n if (!map || typeof map !== 'object') return undefined;\n const value = (map as Record<string, unknown>)[currency];\n return typeof value === 'string' ? value : undefined;\n }\n\n /**\n * Set the payout destination for a single currency. Mutates the live\n * map and returns `this` for chaining. Use {@link clearPayoutAddress}\n * to remove a single entry.\n */\n setPayoutAddress(currency: string, destination: string): this {\n this.payoutAddresses = {\n ...(this.payoutAddresses ?? {}),\n [currency]: destination,\n };\n return this;\n }\n\n /**\n * Remove a single currency entry from the payout map. No-op if the\n * currency was already absent.\n */\n clearPayoutAddress(currency: string): this {\n if (!this.payoutAddresses || !(currency in this.payoutAddresses)) {\n return this;\n }\n const next = { ...this.payoutAddresses };\n delete next[currency];\n this.payoutAddresses = next;\n return this;\n }\n\n /**\n * Normalize an input value (object, pre-serialized JSON string, or\n * `null`/`undefined`) into a plain `Record<string, string>`. Used by\n * the constructor so consumers can pass either shape into\n * `VendorCollection.create({ payoutAddresses: ... })`.\n *\n * Non-string values inside an input object are silently dropped — a\n * `Record<string, string>` invariant is what consumers downstream\n * (`PaymentIntent` builders, payout SDK adapters) rely on.\n */\n private static normalizePayoutAddresses(\n value: unknown,\n ): Record<string, string> {\n if (value == null) return {};\n let parsed: unknown = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value);\n } catch {\n return {};\n }\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {};\n }\n const out: Record<string, string> = {};\n for (const [key, v] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof v === 'string') out[key] = v;\n }\n return out;\n }\n}\n\nexport default Vendor;\n","/**\n * Contract model with STI for different contract types\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n type Meta,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { ContractStatus, ContractType } from '../types/index.js';\nimport { Customer } from './Customer.js';\nimport { Vendor } from './Vendor.js';\n\n/**\n * Legal status transitions for a Contract (and all its STI subtypes), keyed by\n * the prior persisted status. A status re-saved unchanged (no-op) and a\n * brand-new row are always permitted; this map governs *changes* only.\n *\n * Flow: DRAFT → SENT → (ACCEPTED | DECLINED); ACCEPTED → (COMPLETED |\n * CANCELLED). An estimate/order may also be cancelled directly from DRAFT/SENT.\n * DECLINED / COMPLETED / CANCELLED are terminal. This guards against raw\n * mass-assignment forcing, e.g., a DECLINED contract back to ACCEPTED or a\n * COMPLETED one back to DRAFT. See S5 audit #1390.\n */\nconst CONTRACT_STATUS_TRANSITIONS: Record<ContractStatus, ContractStatus[]> = {\n [ContractStatus.DRAFT]: [\n ContractStatus.SENT,\n ContractStatus.ACCEPTED,\n ContractStatus.DECLINED,\n ContractStatus.CANCELLED,\n ],\n [ContractStatus.SENT]: [\n ContractStatus.ACCEPTED,\n ContractStatus.DECLINED,\n ContractStatus.CANCELLED,\n ],\n [ContractStatus.ACCEPTED]: [\n ContractStatus.COMPLETED,\n ContractStatus.CANCELLED,\n ],\n [ContractStatus.DECLINED]: [],\n [ContractStatus.COMPLETED]: [],\n [ContractStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each Contract instance was loaded with,\n * for the save-time transition guard. WeakMap keeps it out of the schema and\n * GCs with the instance — same pattern as the other commerce models.\n */\nconst loadedContractStatus = new WeakMap<Contract, ContractStatus>();\n\n/**\n * Contract is the base class for all commercial agreements.\n *\n * Uses Single Table Inheritance (STI) to support different contract types:\n * - Estimate: Quote/proposal for customer\n * - Order: Customer purchase order\n * - Lease: Rental/lease agreement\n * - Agreement: Service/maintenance agreement\n * - PurchaseOrder: Order to vendor/supplier\n *\n * @example\n * ```typescript\n * // Create a customer order\n * const order = await contracts.create({\n * _meta_type: 'Order',\n * customerId: customer.id,\n * totalAmount: 1500.00,\n * status: ContractStatus.DRAFT\n * });\n *\n * // Create a purchase order to vendor\n * const po = await contracts.create({\n * _meta_type: 'PurchaseOrder',\n * vendorId: vendor.id,\n * totalAmount: 5000.00,\n * reference: 'PO-2024-001'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Contract extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global contracts\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Contract type discriminator (STI)\n */\n contractType: ContractType = ContractType.ORDER;\n\n /**\n * Current status of the contract\n */\n status: ContractStatus = ContractStatus.DRAFT;\n\n /**\n * Customer ID (for sales contracts: Estimate, Order, Agreement)\n */\n @foreignKey(Customer)\n customerId: string = '';\n\n /**\n * Vendor ID (for purchase contracts: PurchaseOrder)\n */\n @foreignKey(Vendor)\n vendorId: string = '';\n\n /**\n * Subtotal before tax\n */\n subtotal: number = 0.0;\n\n /**\n * Tax amount\n */\n taxAmount: number = 0.0;\n\n /**\n * Total amount including tax\n */\n totalAmount: number = 0.0;\n\n /**\n * Currency code (ISO 4217)\n */\n currency: string = 'USD';\n\n /**\n * Date contract was issued\n */\n issueDate: Date = new Date();\n\n /**\n * Payment due date\n */\n dueDate: Date | null = null;\n\n /**\n * Expiry date (for estimates/quotes)\n */\n expiryDate: Date | null = null;\n\n /**\n * External reference number (PO number, quote number, etc.)\n */\n reference: string = '';\n\n /**\n * Internal notes\n */\n notes: string = '';\n\n /**\n * Terms and conditions\n */\n terms: string = '';\n\n /**\n * Sales channel that owns this contract.\n *\n * Plain string so consumers can model their own channels without an enum\n * change (e.g. `dtc-web`, `wholesale-b2b`, `pos-store-1`, `marketplace-faire`).\n * Empty string means \"unattributed / legacy\".\n */\n channelId: string = '';\n\n /**\n * STI discriminator field\n */\n static readonly _stiField = 'contractType';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractType !== undefined)\n this.contractType = options.contractType;\n if (options.status !== undefined) this.status = options.status;\n if (options.customerId !== undefined) this.customerId = options.customerId;\n if (options.vendorId !== undefined) this.vendorId = options.vendorId;\n if (options.subtotal !== undefined) this.subtotal = options.subtotal;\n if (options.taxAmount !== undefined) this.taxAmount = options.taxAmount;\n if (options.totalAmount !== undefined)\n this.totalAmount = options.totalAmount;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.issueDate !== undefined) this.issueDate = options.issueDate;\n if (options.dueDate !== undefined) this.dueDate = options.dueDate;\n if (options.expiryDate !== undefined) this.expiryDate = options.expiryDate;\n if (options.reference !== undefined) this.reference = options.reference;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.terms !== undefined) this.terms = options.terms;\n if (options.channelId !== undefined) this.channelId = options.channelId;\n }\n\n /**\n * Check if contract is in draft state\n */\n isDraft(): boolean {\n return this.status === ContractStatus.DRAFT;\n }\n\n /**\n * Check if contract is accepted\n */\n isAccepted(): boolean {\n return this.status === ContractStatus.ACCEPTED;\n }\n\n /**\n * Check if contract is completed\n */\n isCompleted(): boolean {\n return this.status === ContractStatus.COMPLETED;\n }\n\n /**\n * Check if contract is expired (for estimates)\n */\n isExpired(): boolean {\n if (!this.expiryDate) return false;\n return new Date() > this.expiryDate;\n }\n\n /**\n * Check if contract is overdue\n */\n isOverdue(): boolean {\n if (!this.dueDate) return false;\n if (this.status === ContractStatus.COMPLETED) return false;\n return new Date() > this.dueDate;\n }\n\n /**\n * Calculate and update totals from line items\n */\n async recalculateTotals(): Promise<void> {\n // This would typically query ContractLineItems and sum them\n // For now, just ensure totalAmount = subtotal + taxAmount\n this.totalAmount = this.subtotal + this.taxAmount;\n }\n\n /**\n * Capture the status the row was loaded with so the save-time transition\n * guard can reject illegal status flips made via raw field assignment\n * (mass-assignment on the generated update route, a stale caller, etc.).\n * Only persisted rows carry a prior status.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedContractStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Validate the status transition before persisting, then save. Inherited by\n * every STI subtype (Estimate/Order/Lease/.../LicenseSale), so a forged\n * `status` on any of them is caught here. LicenseSale layers its own\n * rights-immutability guard on top via `super.save()`. See S5 audit #1390.\n */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertContractStatusTransition(prior);\n const result = (await super.save()) as this;\n loadedContractStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Reject an illegal status flip done via raw assignment. No-op transitions\n * and brand-new rows are always allowed.\n */\n protected assertContractStatusTransition(\n prior: ContractStatus | undefined,\n ): 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 = CONTRACT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Contract ${this.reference || this.id}: illegal status transition ` +\n `'${prior}' → '${this.status}'.`,\n );\n }\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status (S5 audit #1390 round 5, codex\n * MEDIUM#2). The WeakMap is only populated when {@link initialize} loaded the\n * row from the DB; it is empty for an instance built via\n * `collection.create({ id: <existing>, _skipLoad: true })` — the upsert path\n * that lets a caller write onto an existing row without hydrating it. Trusting\n * an empty WeakMap there would treat the write as a brand-new row and skip the\n * transition guard entirely (a poisonable prior-state). Mirrors the\n * authoritative-prior-load applied to Payment/PaymentIntent/Invoice/Payout.\n *\n * So when this instance carries an `id`, read the persisted row straight from\n * the database and treat its `status` as the prior — a create-onto-existing is\n * an update. Only when no row exists in the DB (truly new) do we fall back to\n * the WeakMap (which is also empty then), i.e. `undefined` = genuinely new.\n * The raw row's `status` column is single-word (no snake_case transform).\n */\n protected async resolvePriorStatus(): Promise<ContractStatus | undefined> {\n if (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 ContractStatus;\n }\n } catch {\n // DB not ready / table absent — fall through to the in-memory record.\n }\n }\n return loadedContractStatus.get(this);\n }\n}\n\n// Every STI child below MUST repeat the `@TenantScoped` decoration. The\n// tenancy registry keys off the concrete className passed by the\n// collection, not the inheritance chain — `OrderCollection.list()` sends\n// `'Order'` to the interceptor, which then misses the `'Contract'`\n// registration and skips tenant auto-filter/auto-populate. Without an\n// explicit `@TenantScoped` on each subclass, saves end up with\n// `tenantId: null` (the row becomes global / invisible to tenant-scoped\n// queries) and lists return rows across tenants.\n\n/**\n * Estimate - Quote or proposal for a customer\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Estimate extends Contract {\n override contractType = ContractType.ESTIMATE;\n}\n\n/**\n * Order - Customer purchase order\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Order extends Contract {\n override contractType = ContractType.ORDER;\n}\n\n/**\n * Lease - Rental or lease agreement\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Lease extends Contract {\n override contractType = ContractType.LEASE;\n}\n\n/**\n * Agreement - Service or maintenance agreement\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Agreement extends Contract {\n override contractType = ContractType.AGREEMENT;\n}\n\n/**\n * PurchaseOrder - Order sent to vendor/supplier\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class PurchaseOrder extends Contract {\n override contractType = ContractType.PURCHASE_ORDER;\n}\n\n/**\n * WholesaleOrder - B2B order placed by a wholesale customer.\n *\n * Behaves like an Order but is conventionally created against a customer with\n * `customerType: 'wholesale'`, uses NET-30/60 payment terms, and is delivered\n * via the wholesale-portal channel rather than retail checkout.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class WholesaleOrder extends Contract {\n override contractType = ContractType.WHOLESALE_ORDER;\n}\n\n/**\n * ProductionOrder - instruction to commission goods from an internal or\n * external factory.\n *\n * The manufacturing equivalent of a {@link PurchaseOrder} — but instead of\n * buying finished inventory, you're paying to *make* it. A ProductionOrder\n * consumes raw materials per a Bill of Materials (see\n * `@happyvertical/smrt-manufacturing`) and produces finished SKU stock when\n * posted (see `@happyvertical/smrt-inventory`).\n *\n * Carries two STI-meta fields that `ProductionService.consumeMaterials` /\n * `runProduction` read from the loaded row to resolve which BOM to walk:\n *\n * - `productId` — plain string ref to the upstream `Product` (or any\n * STI subtype, e.g. apparel `Style`). Used to look up the *active*\n * BOM via `BomService.findActiveForProduct` when no explicit\n * revision was pinned at the time the order was placed.\n * - `bomId` — optional pin to a specific BOM revision. When set, the\n * manufacturing service consumes against that exact revision even\n * if a newer BOM has become active in the meantime (keeps a\n * posted run from silently switching recipes mid-flight).\n *\n * Both fields are `Meta<string>` so the scanner routes them into the\n * shared `_meta_data` JSON column on the Contract STI table instead of\n * widening the base schema with manufacturing-specific columns.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class ProductionOrder extends Contract {\n override contractType = ContractType.PRODUCTION_ORDER;\n\n /**\n * Plain string reference to the upstream product the order is asked\n * to make. Cross-package id; never `@foreignKey()`. Required by\n * `ProductionService.consumeMaterials` unless `bomId` is supplied.\n */\n productId: Meta<string> = '';\n\n /**\n * Optional explicit BOM revision the order locked to at posting\n * time. When set, manufacturing uses this exact row even if the\n * product's active BOM has since changed. When empty, falls back\n * to the active BOM for `productId`.\n */\n bomId: Meta<string> = '';\n}\n\n/**\n * Cart - transient order-in-progress for a shopper.\n *\n * Persisted as a Contract so it can hold the same line items, totals, and\n * customer reference as a real Order, then convert in place (the application\n * promotes the row from `_meta_type: Cart` to `_meta_type: Order` at checkout\n * rather than copying data between tables).\n *\n * A Cart starts in {@link ContractStatus.DRAFT} via the base `Contract.status`\n * default — the subclass doesn't need its own override. Application code\n * is responsible for promoting the row to `ContractStatus.PENDING` /\n * `ACCEPTED` at checkout time; the framework doesn't enforce a state\n * machine on the cart → order transition.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Cart extends Contract {\n override contractType = ContractType.CART;\n}\n\n/**\n * Snapshot of the rights granted by a {@link LicenseSale}. The fields are\n * intentionally typed (not a generic JSON blob) so they survive schema\n * migrations and downstream consumers (PDF templating, hash-registry\n * publishers) can rely on the shape without re-parsing.\n *\n * Each field is opaque text — the values that make sense vary by\n * industry. A stock-media marketplace might use:\n *\n * - `medium`: `web,print,social`\n * - `distributionScope`: `worldwide` / `territorial`\n * - `exclusivity`: `exclusive` / `non-exclusive`\n * - `duration`: `perpetual` / `12-months` / `event-only`\n * - `territory`: ISO-3166 list or `worldwide`\n *\n * Booleans (`sublicensing`, `derivatives`) are typed as such; everything\n * else is a string so the model doesn't dictate vocabulary.\n */\nexport interface LicenseRightsSnapshot {\n medium: string;\n distributionScope: string;\n exclusivity: string;\n duration: string;\n territory: string;\n sublicensing: boolean;\n derivatives: boolean;\n}\n\n/**\n * Module-scoped WeakMap of \"rights snapshot the row was loaded with\".\n *\n * Used by {@link LicenseSale}'s immutability guard. We can't store the\n * captured snapshot as an instance field because the AST scanner would\n * pick it up as a persisted column; we can't store it as a `Meta<T>`\n * because then it'd round-trip with the row and the comparison would\n * always tautologically succeed. A WeakMap keyed by the instance is\n * the cleanest scope: no schema interaction, garbage-collected with\n * the instance.\n */\nconst issuedRightsSnapshot = new WeakMap<LicenseSale, string>();\n\n/**\n * LicenseSale — industry-neutral licensing primitive.\n *\n * Sits alongside the other Contract STI subtypes (Estimate, Order,\n * Lease, Agreement, PurchaseOrder, WholesaleOrder, ProductionOrder,\n * Cart) and represents a single sale of rights for a fee. A\n * stock-media marketplace selling a one-time license, a music-\n * licensing platform issuing a sync license, a code-asset marketplace\n * granting a redistribution right — all use the same primitive.\n *\n * Immutability invariant: once a `LicenseSale` is saved with\n * {@link ContractStatus.ACCEPTED}, its rights snapshot becomes\n * frozen. Re-saving with mutated rights throws. The only legal\n * transition out of ACCEPTED is `revoke()` (which moves status to\n * CANCELLED without changing the rights). If a different snapshot\n * is genuinely needed, the operator's contract is to issue a *new*\n * LicenseSale row and let the old one stand as historical record.\n *\n * All subtype-specific fields are declared as `Meta<T>` so the\n * scanner routes them into the shared `_meta_data` JSON column on\n * the `contracts` STI table — no migration of the base schema needed\n * to land this subtype.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class LicenseSale extends Contract {\n override contractType = ContractType.LICENSE_SALE;\n\n /**\n * Plain string reference to the upstream `Sku` (from\n * `@happyvertical/smrt-products`) that this license was sold against.\n * Cross-package id; never `@foreignKey()`.\n */\n skuId: Meta<string> = '';\n\n /**\n * Plain string reference to the `Payment` that satisfied the\n * purchase. Cross-model reference matches the rest of the\n * package's convention.\n */\n paymentId: Meta<string> = '';\n\n /**\n * The licensee's email address — the primary identity carried\n * forward into PDF generation and any downstream hash-registry\n * publication. Required; without it, downstream rights cannot be\n * attributed.\n */\n licenseeEmail: Meta<string> = '';\n\n /**\n * Optional legal-entity name of the licensee for purchases that\n * warrant it (typically high-tier B2B licenses). Empty string for\n * individual-buyer licenses where the email *is* the legal identity.\n */\n licenseeLegalEntity: Meta<string> = '';\n\n /**\n * Optional ISO-3166 jurisdiction code of the licensee, captured for\n * tax / disclosure / governing-law purposes. Empty when not\n * required.\n */\n licenseeJurisdiction: Meta<string> = '';\n\n // -------- Rights snapshot (immutable once issued) --------\n\n /** Permitted media — `web,print,social`, `broadcast`, etc. */\n rightsMedium: Meta<string> = '';\n\n /** Distribution scope — `worldwide`, `territorial`, `private`, etc. */\n rightsDistributionScope: Meta<string> = '';\n\n /** Exclusivity — `exclusive` / `non-exclusive` / `co-exclusive`. */\n rightsExclusivity: Meta<string> = '';\n\n /** Duration — `perpetual` / `12-months` / `event-only`, etc. */\n rightsDuration: Meta<string> = '';\n\n /** Territory — ISO-3166 list, `worldwide`, region name, etc. */\n rightsTerritory: Meta<string> = '';\n\n /** Whether the licensee may sublicense the rights. */\n rightsSublicensing: Meta<boolean> = false;\n\n /** Whether the licensee may create derivative works. */\n rightsDerivatives: Meta<boolean> = false;\n\n // -------- Artefacts --------\n\n /** URL of the signed PDF (typically a cloud-storage public URL). */\n pdfUrl: Meta<string> = '';\n\n /**\n * SHA-256 of the signed PDF bytes (hex string, no `0x` prefix).\n * Lets downstream consumers verify the PDF the licensee holds is\n * the one this row was issued for, without re-fetching.\n */\n pdfHash: Meta<string> = '';\n\n /**\n * Optional reference to an on-chain hash-registry entry — set when\n * the LicenseSale's `pdfHash` has been anchored on a public chain\n * for tamper-evidence. Format is whatever the hash-registry\n * service emits (e.g. `chain:tx-hash:log-index`). Empty for\n * deployments that don't publish hashes on-chain.\n */\n onChainHashRegistryRef: Meta<string> = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.skuId !== undefined) this.skuId = options.skuId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.licenseeEmail !== undefined)\n this.licenseeEmail = options.licenseeEmail;\n if (options.licenseeLegalEntity !== undefined)\n this.licenseeLegalEntity = options.licenseeLegalEntity;\n if (options.licenseeJurisdiction !== undefined)\n this.licenseeJurisdiction = options.licenseeJurisdiction;\n if (options.rightsMedium !== undefined)\n this.rightsMedium = options.rightsMedium;\n if (options.rightsDistributionScope !== undefined)\n this.rightsDistributionScope = options.rightsDistributionScope;\n if (options.rightsExclusivity !== undefined)\n this.rightsExclusivity = options.rightsExclusivity;\n if (options.rightsDuration !== undefined)\n this.rightsDuration = options.rightsDuration;\n if (options.rightsTerritory !== undefined)\n this.rightsTerritory = options.rightsTerritory;\n if (options.rightsSublicensing !== undefined)\n this.rightsSublicensing = options.rightsSublicensing;\n if (options.rightsDerivatives !== undefined)\n this.rightsDerivatives = options.rightsDerivatives;\n if (options.pdfUrl !== undefined) this.pdfUrl = options.pdfUrl;\n if (options.pdfHash !== undefined) this.pdfHash = options.pdfHash;\n if (options.onChainHashRegistryRef !== undefined)\n this.onChainHashRegistryRef = options.onChainHashRegistryRef;\n }\n\n /**\n * Once `super.initialize()` has applied the framework's option-\n * override pass, capture the rights snapshot if (and only if) the\n * row arrived already issued. That's how the immutability guard\n * tells \"freshly drafting a license\" (snapshot still mutable) apart\n * from \"loaded an already-issued row\" (snapshot frozen).\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (this.status === ContractStatus.ACCEPTED && (await this.isSaved())) {\n issuedRightsSnapshot.set(this, this.serializeRightsSnapshot());\n }\n return this;\n }\n\n /**\n * Return the current rights snapshot as a typed object. Read-only\n * for callers — mutation should go through assignment to the\n * individual `rights*` fields, gated by the issued-immutability\n * guard.\n */\n getRightsSnapshot(): LicenseRightsSnapshot {\n return {\n medium: this.rightsMedium,\n distributionScope: this.rightsDistributionScope,\n exclusivity: this.rightsExclusivity,\n duration: this.rightsDuration,\n territory: this.rightsTerritory,\n sublicensing: this.rightsSublicensing,\n derivatives: this.rightsDerivatives,\n };\n }\n\n /**\n * Revoke an issued license — moves status to `CANCELLED` without\n * touching the rights snapshot (which stays frozen). Throws if\n * called on a non-issued license; a draft license should be\n * deleted, not revoked.\n */\n revoke(): void {\n if (this.status !== ContractStatus.ACCEPTED) {\n throw new Error(\n `LicenseSale ${this.id ?? '<new>'}: cannot revoke from status '${this.status}' — only ACCEPTED licenses can be revoked`,\n );\n }\n this.status = ContractStatus.CANCELLED;\n }\n\n /**\n * Save with the immutability check: if the row was loaded with\n * `ACCEPTED` status, the rights snapshot at save time must match\n * the snapshot at load time. Anything else means an out-of-band\n * mutation crept in and we surface it instead of letting the row\n * drift silently.\n *\n * Newly-drafted licenses (where the row was constructed without\n * `ACCEPTED` status) capture their snapshot at the moment the\n * caller transitions to `ACCEPTED` and saves — `validateImmutable`\n * picks that up post-save on the next call.\n */\n override async save(): Promise<this> {\n this.validateImmutable();\n const result = (await super.save()) as this;\n // If this save transitioned the row to ACCEPTED, freeze the\n // snapshot for future saves.\n if (\n this.status === ContractStatus.ACCEPTED &&\n !issuedRightsSnapshot.has(this)\n ) {\n issuedRightsSnapshot.set(this, this.serializeRightsSnapshot());\n }\n return result;\n }\n\n /**\n * Compare the current rights against the captured snapshot\n * (populated either at load time by `initialize` or at first\n * issuance by `save`). Throws on mismatch.\n */\n private validateImmutable(): void {\n const captured = issuedRightsSnapshot.get(this);\n if (!captured) return;\n const current = this.serializeRightsSnapshot();\n if (captured !== current) {\n throw new Error(\n `LicenseSale ${this.id ?? '<new>'}: rights snapshot is immutable once issued (status=ACCEPTED). ` +\n 'Use revoke() to cancel the existing license and issue a new one with the updated rights.',\n );\n }\n }\n\n private serializeRightsSnapshot(): string {\n // Stable key ordering so a re-serialization after a no-op\n // assignment doesn't trip the equality check.\n return JSON.stringify({\n medium: this.rightsMedium,\n distributionScope: this.rightsDistributionScope,\n exclusivity: this.rightsExclusivity,\n duration: this.rightsDuration,\n territory: this.rightsTerritory,\n sublicensing: this.rightsSublicensing,\n derivatives: this.rightsDerivatives,\n });\n }\n}\n\nexport default Contract;\n","/**\n * ContractCollection - Collection manager for Contract objects (STI)\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Contract } from '../models/Contract.js';\nimport { ContractStatus, ContractType } from '../types/index.js';\n\nexport class ContractCollection extends SmrtCollection<Contract> {\n static readonly _itemClass = Contract;\n\n /**\n * Find contracts by customer\n *\n * @param customerId - Customer ID\n * @returns Array of contracts\n */\n async findByCustomer(customerId: string): Promise<Contract[]> {\n return await this.list({\n where: { customerId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find contracts by vendor\n *\n * @param vendorId - Vendor ID\n * @returns Array of contracts\n */\n async findByVendor(vendorId: string): Promise<Contract[]> {\n return await this.list({\n where: { vendorId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find contracts by type (STI)\n *\n * @param contractType - Contract type\n * @returns Array of contracts\n */\n async findByType(contractType: ContractType): Promise<Contract[]> {\n return await this.list({\n where: { contractType },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find contracts by status\n *\n * @param status - Contract status\n * @returns Array of contracts\n */\n async findByStatus(status: ContractStatus): Promise<Contract[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all draft contracts\n *\n * @returns Array of draft contracts\n */\n async findDrafts(): Promise<Contract[]> {\n return await this.findByStatus(ContractStatus.DRAFT);\n }\n\n /**\n * Find all accepted contracts\n *\n * @returns Array of accepted contracts\n */\n async findAccepted(): Promise<Contract[]> {\n return await this.findByStatus(ContractStatus.ACCEPTED);\n }\n\n /**\n * Find overdue contracts\n *\n * @returns Array of overdue contracts\n */\n async findOverdue(): Promise<Contract[]> {\n const now = new Date().toISOString();\n // Get all contracts past due date that aren't completed or cancelled\n const all = await this.list({\n where: {\n 'dueDate <': now,\n },\n orderBy: 'dueDate ASC',\n });\n return all.filter(\n (c) =>\n c.status !== ContractStatus.COMPLETED &&\n c.status !== ContractStatus.CANCELLED,\n );\n }\n\n /**\n * Find expired estimates/quotes\n *\n * @returns Array of expired estimates\n */\n async findExpired(): Promise<Contract[]> {\n const now = new Date().toISOString();\n // Get estimates past expiry date that aren't accepted, declined, or cancelled\n const estimates = await this.list({\n where: {\n contractType: ContractType.ESTIMATE,\n 'expiryDate <': now,\n },\n orderBy: 'expiryDate ASC',\n });\n return estimates.filter(\n (c) =>\n c.status !== ContractStatus.ACCEPTED &&\n c.status !== ContractStatus.DECLINED &&\n c.status !== ContractStatus.CANCELLED,\n );\n }\n\n /**\n * Find contracts in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of contracts\n */\n async findByDateRange(startDate: Date, endDate: Date): Promise<Contract[]> {\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 // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all contracts belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of contracts for the tenant\n */\n async findByTenant(tenantId: string): Promise<Contract[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global contracts (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 contracts\n */\n async findGlobal(): Promise<Contract[]> {\n return queryGlobal<Contract>(this);\n }\n\n /**\n * Find contracts for a tenant including global contracts.\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 contracts\n */\n async findWithGlobals(tenantId: string): Promise<Contract[]> {\n return queryWithGlobals<Contract>(\n this,\n tenantId,\n 'Contract.findWithGlobals',\n );\n }\n}\n","/**\n * ContractLineItem model - individual items on a contract\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * ContractLineItem represents a single line item on a contract.\n *\n * Line items contain the details of what is being sold/purchased,\n * including quantity, pricing, and optional subscription details.\n *\n * @example\n * ```typescript\n * const lineItem = await lineItems.create({\n * contractId: order.id,\n * description: 'Widget Pro',\n * quantity: 10,\n * unitPrice: 49.99,\n * taxRate: 0.08\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ContractLineItem extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global line items\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent contract\n */\n @foreignKey('Contract')\n contractId: string = '';\n\n /**\n * Item description\n */\n description: string = '';\n\n /**\n * Quantity ordered\n */\n quantity: number = 1.0;\n\n /**\n * Unit price before discount\n */\n unitPrice: number = 0.0;\n\n /**\n * Discount amount (flat, not percentage)\n */\n discount: number = 0.0;\n\n /**\n * Tax rate as decimal (e.g., 0.08 for 8%)\n */\n taxRate: number = 0.0;\n\n /**\n * Calculated line amount (qty * price - discount + tax)\n */\n amount: number = 0.0;\n\n /**\n * Optional product reference (cross-package, plain string)\n */\n @crossPackageRef('@happyvertical/smrt-products:Product')\n productId: string = '';\n\n /**\n * SKU or item code\n */\n sku: string = '';\n\n /**\n * Start date (for lease/subscription items)\n */\n startDate: Date | null = null;\n\n /**\n * End date (for lease/subscription items)\n */\n endDate: Date | null = null;\n\n /**\n * Billing period (for subscriptions: 'monthly', 'yearly', etc.)\n */\n billingPeriod: string = '';\n\n /**\n * Sort order within the contract\n */\n sortOrder: number = 0;\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.description !== undefined)\n this.description = options.description;\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.taxRate !== undefined) this.taxRate = options.taxRate;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.productId !== undefined) this.productId = options.productId;\n if (options.sku !== undefined) this.sku = options.sku;\n if (options.startDate !== undefined) this.startDate = options.startDate;\n if (options.endDate !== undefined) this.endDate = options.endDate;\n if (options.billingPeriod !== undefined)\n this.billingPeriod = options.billingPeriod;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n }\n\n /**\n * Calculate the line amount\n */\n calculateAmount(): number {\n const subtotal = this.quantity * this.unitPrice - this.discount;\n const tax = subtotal * this.taxRate;\n return subtotal + tax;\n }\n\n /**\n * Check if this is a subscription/recurring item\n */\n isRecurring(): boolean {\n return !!this.billingPeriod && !!this.startDate;\n }\n\n /**\n * Check if subscription is active\n */\n isSubscriptionActive(): boolean {\n if (!this.isRecurring()) return false;\n if (!this.startDate) return false;\n\n const now = new Date();\n if (now < this.startDate) return false;\n if (this.endDate && now > this.endDate) return false;\n\n return true;\n }\n}\n\nexport default ContractLineItem;\n","/**\n * ContractLineItemCollection - Collection manager for ContractLineItem objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { ContractLineItem } from '../models/ContractLineItem.js';\n\nexport class ContractLineItemCollection extends SmrtCollection<ContractLineItem> {\n static readonly _itemClass = ContractLineItem;\n\n /**\n * Find contract line items by their parent contract.\n *\n * @param contractId - Contract ID\n * @returns Array of contract line items, sorted by sortOrder\n */\n async findByContract(contractId: string): Promise<ContractLineItem[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'sortOrder ASC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all contract line items belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of contract line items for the tenant\n */\n async findByTenant(tenantId: string): Promise<ContractLineItem[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global contract 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 contract line items\n */\n async findGlobal(): Promise<ContractLineItem[]> {\n return queryGlobal<ContractLineItem>(this);\n }\n\n /**\n * Find contract 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 contract line items\n */\n async findWithGlobals(tenantId: string): Promise<ContractLineItem[]> {\n return queryWithGlobals<ContractLineItem>(\n this,\n tenantId,\n 'ContractLineItem.findWithGlobals',\n );\n }\n}\n","/**\n * CustomerCollection - Collection manager for Customer objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Customer } from '../models/Customer.js';\nimport { CustomerStatus } from '../types/index.js';\n\nexport class CustomerCollection extends SmrtCollection<Customer> {\n static readonly _itemClass = Customer;\n\n /**\n * Find customers by profile ID\n *\n * @param profileId - Profile ID from smrt-profiles\n * @returns Array of customers linked to this profile\n */\n async findByProfile(profileId: string): Promise<Customer[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active customers\n *\n * @returns Array of active customers\n */\n async findActive(): Promise<Customer[]> {\n return await this.list({\n where: { status: CustomerStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find customers by status\n *\n * @param status - Customer status\n * @returns Array of customers\n */\n async findByStatus(status: CustomerStatus): Promise<Customer[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Get or create a customer for a profile\n *\n * @param profileId - Profile ID\n * @param defaults - Default values if creating\n * @returns Customer\n */\n async getOrCreateForProfile(\n profileId: string,\n defaults: Partial<{\n creditLimit: number;\n paymentTerms: string;\n }> = {},\n ): Promise<Customer> {\n const existing = await this.findByProfile(profileId);\n if (existing.length > 0) {\n return existing[0];\n }\n\n const customer = await this.create({\n profileId,\n creditLimit: defaults.creditLimit ?? 0,\n paymentTerms: defaults.paymentTerms ?? '',\n status: CustomerStatus.ACTIVE,\n });\n await customer.save();\n return customer;\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all customers belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of customers for the tenant\n */\n async findByTenant(tenantId: string): Promise<Customer[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global customers (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 customers\n */\n async findGlobal(): Promise<Customer[]> {\n return queryGlobal<Customer>(this);\n }\n\n /**\n * Find customers for a tenant including global customers.\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 customers\n */\n async findWithGlobals(tenantId: string): Promise<Customer[]> {\n return queryWithGlobals<Customer>(\n this,\n tenantId,\n 'Customer.findWithGlobals',\n );\n }\n}\n","/**\n * Fulfillment model - tracks delivery of contract items\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type Address,\n FulfillmentStatus,\n FulfillmentType,\n} from '../types/index.js';\n\n/**\n * Legal status transitions for a Fulfillment, keyed by the prior persisted\n * status. A status re-saved unchanged (no-op) and a brand-new row are always\n * permitted; this map governs *changes* only.\n *\n * Forward order: PENDING → PROCESSING → SHIPPED → DELIVERED, but progress may\n * SKIP intermediate states — not every order has a PROCESSING step, so\n * PENDING → SHIPPED and PENDING/PROCESSING → DELIVERED are deliberately legal.\n * The map only forbids moving BACKWARD (e.g. SHIPPED → PENDING) and leaving a\n * terminal state. CANCELLED is reachable from any non-terminal state.\n * DELIVERED and CANCELLED are terminal. This guards against raw mass-assignment\n * (the open `api:{create,update}` surface, or a stale caller) forcing, e.g., a\n * DELIVERED shipment back to PENDING or a CANCELLED one back into SHIPPED.\n * Mirrors the Contract/Payment/Payout guards added in S5 audit #1390 —\n * Fulfillment was the one money/state model missed.\n */\nconst FULFILLMENT_STATUS_TRANSITIONS: Record<\n FulfillmentStatus,\n FulfillmentStatus[]\n> = {\n [FulfillmentStatus.PENDING]: [\n FulfillmentStatus.PROCESSING,\n FulfillmentStatus.SHIPPED,\n FulfillmentStatus.DELIVERED,\n FulfillmentStatus.CANCELLED,\n ],\n [FulfillmentStatus.PROCESSING]: [\n FulfillmentStatus.SHIPPED,\n FulfillmentStatus.DELIVERED,\n FulfillmentStatus.CANCELLED,\n ],\n [FulfillmentStatus.SHIPPED]: [\n FulfillmentStatus.DELIVERED,\n FulfillmentStatus.CANCELLED,\n ],\n // Terminal states: no outbound transitions.\n [FulfillmentStatus.DELIVERED]: [],\n [FulfillmentStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each Fulfillment instance was loaded with,\n * for the save-time transition guard. A WeakMap keeps it out of the schema and\n * GCs with the instance — same pattern as the other commerce models.\n */\nconst loadedFulfillmentStatus = new WeakMap<Fulfillment, FulfillmentStatus>();\n\n/**\n * Fulfillment tracks the delivery or completion of contract items.\n *\n * A contract can have multiple fulfillments (e.g., partial shipments).\n * Each fulfillment tracks what was delivered and when.\n *\n * @example\n * ```typescript\n * const fulfillment = await fulfillments.create({\n * contractId: order.id,\n * fulfillmentType: FulfillmentType.SHIPMENT,\n * trackingNumber: 'UPS-123456789',\n * carrier: 'UPS',\n * shippingAddress: {\n * street1: '123 Main St',\n * city: 'Anytown',\n * state: 'CA',\n * postalCode: '90210',\n * country: 'US'\n * }\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Fulfillment extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global fulfillments\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent contract being fulfilled\n */\n @foreignKey('Contract')\n contractId: string = '';\n\n /**\n * Type of fulfillment\n */\n fulfillmentType: FulfillmentType = FulfillmentType.SHIPMENT;\n\n /**\n * Current fulfillment status\n */\n status: FulfillmentStatus = FulfillmentStatus.PENDING;\n\n /**\n * Tracking number from carrier\n */\n trackingNumber: string = '';\n\n /**\n * Shipping carrier name\n */\n carrier: string = '';\n\n /**\n * Shipping destination address\n */\n shippingAddress: Address = {};\n\n /**\n * When the shipment was sent\n */\n shippedAt: Date | null = null;\n\n /**\n * When the shipment was delivered\n */\n deliveredAt: Date | null = null;\n\n /**\n * Estimated delivery date\n */\n estimatedDelivery: Date | null = null;\n\n /**\n * Notes about this fulfillment\n */\n notes: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.fulfillmentType !== undefined)\n this.fulfillmentType = options.fulfillmentType;\n if (options.status !== undefined) this.status = options.status;\n if (options.trackingNumber !== undefined)\n this.trackingNumber = options.trackingNumber;\n if (options.carrier !== undefined) this.carrier = options.carrier;\n if (options.shippingAddress !== undefined)\n this.shippingAddress = options.shippingAddress;\n if (options.shippedAt !== undefined) this.shippedAt = options.shippedAt;\n if (options.deliveredAt !== undefined)\n this.deliveredAt = options.deliveredAt;\n if (options.estimatedDelivery !== undefined)\n this.estimatedDelivery = options.estimatedDelivery;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Capture the status the row was loaded with so the save-time transition\n * guard can reject illegal status flips made via raw field assignment\n * (mass-assignment on the generated update route, a stale caller, etc.).\n * Only persisted rows carry a prior status.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedFulfillmentStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Validate the status transition before persisting, then save. Blocks a\n * forged `status` written via raw mass-assignment on the open\n * `api:{create,update}` surface. See S5 audit #1390 follow-up.\n */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n const result = (await super.save()) as this;\n loadedFulfillmentStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Reject an illegal status flip done via raw assignment. No-op transitions\n * and brand-new rows are always allowed.\n */\n private assertStatusTransition(prior: FulfillmentStatus | 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 = FULFILLMENT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Fulfillment ${this.trackingNumber || this.id}: illegal status ` +\n `transition '${prior}' → '${this.status}'. Use the guarded helpers ` +\n '(markShipped / markDelivered / cancel).',\n );\n }\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status. The WeakMap is only populated when\n * {@link initialize} loaded the row; it is empty for an instance built via\n * `collection.create({ id: <existing>, _skipLoad: true })` (the upsert path\n * that writes onto an existing row without hydrating it). Trusting an empty\n * WeakMap there would treat the write as a brand-new row and skip the guard.\n * So when this instance carries an `id`, read the persisted row straight from\n * the DB and treat its `status` as the prior. `undefined` = genuinely new.\n * Mirrors the authoritative-prior-load on Contract/Payment/Invoice/Payout.\n */\n private async resolvePriorStatus(): Promise<FulfillmentStatus | undefined> {\n if (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 FulfillmentStatus;\n }\n } catch {\n // DB not ready / table absent — fall through to the in-memory record.\n }\n }\n return loadedFulfillmentStatus.get(this);\n }\n\n /**\n * Check if fulfillment is complete\n */\n isDelivered(): boolean {\n return this.status === FulfillmentStatus.DELIVERED;\n }\n\n /**\n * Check if fulfillment is in transit\n */\n isInTransit(): boolean {\n return this.status === FulfillmentStatus.SHIPPED;\n }\n\n /**\n * Check if fulfillment is pending\n */\n isPending(): boolean {\n return this.status === FulfillmentStatus.PENDING;\n }\n\n /**\n * Assert the current in-memory status may legally transition to `next`,\n * surfacing the illegal-transition error at the mutation call site rather\n * than deferring it to save(). A no-op (already in `next`) is allowed.\n */\n private assertCanTransitionTo(next: FulfillmentStatus): void {\n if (this.status === next) return;\n const allowed = FULFILLMENT_STATUS_TRANSITIONS[this.status] ?? [];\n if (!allowed.includes(next)) {\n throw new Error(\n `Fulfillment ${this.trackingNumber || this.id || '<new>'}: cannot move ` +\n `from '${this.status}' to '${next}'.`,\n );\n }\n }\n\n /**\n * Mark as shipped. Rejected from a terminal state (DELIVERED / CANCELLED).\n */\n markShipped(trackingNumber?: string, carrier?: string): void {\n this.assertCanTransitionTo(FulfillmentStatus.SHIPPED);\n this.status = FulfillmentStatus.SHIPPED;\n this.shippedAt = new Date();\n if (trackingNumber) this.trackingNumber = trackingNumber;\n if (carrier) this.carrier = carrier;\n }\n\n /**\n * Mark as delivered. Rejected from CANCELLED (a cancelled shipment can't be\n * delivered).\n */\n markDelivered(): void {\n this.assertCanTransitionTo(FulfillmentStatus.DELIVERED);\n this.status = FulfillmentStatus.DELIVERED;\n this.deliveredAt = new Date();\n }\n\n /**\n * Cancel fulfillment. Rejected once DELIVERED (a delivered shipment can't be\n * cancelled — model a return/refund elsewhere instead).\n */\n cancel(): void {\n this.assertCanTransitionTo(FulfillmentStatus.CANCELLED);\n this.status = FulfillmentStatus.CANCELLED;\n }\n}\n\nexport default Fulfillment;\n","/**\n * FulfillmentCollection - Collection manager for Fulfillment objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Fulfillment } from '../models/Fulfillment.js';\nimport { FulfillmentStatus, type FulfillmentType } from '../types/index.js';\n\nexport class FulfillmentCollection extends SmrtCollection<Fulfillment> {\n static readonly _itemClass = Fulfillment;\n\n /**\n * Find fulfillments by contract\n *\n * @param contractId - Contract ID\n * @returns Array of fulfillments\n */\n async findByContract(contractId: string): Promise<Fulfillment[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find fulfillments by status\n *\n * @param status - Fulfillment status\n * @returns Array of fulfillments\n */\n async findByStatus(status: FulfillmentStatus): Promise<Fulfillment[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find fulfillments by type\n *\n * @param fulfillmentType - Fulfillment type\n * @returns Array of fulfillments\n */\n async findByType(fulfillmentType: FulfillmentType): Promise<Fulfillment[]> {\n return await this.list({\n where: { fulfillmentType },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all pending fulfillments\n *\n * @returns Array of pending fulfillments\n */\n async findPending(): Promise<Fulfillment[]> {\n return await this.findByStatus(FulfillmentStatus.PENDING);\n }\n\n /**\n * Find all in-transit shipments\n *\n * @returns Array of shipped fulfillments\n */\n async findInTransit(): Promise<Fulfillment[]> {\n return await this.findByStatus(FulfillmentStatus.SHIPPED);\n }\n\n /**\n * Find by tracking number\n *\n * @param trackingNumber - Tracking number\n * @returns Fulfillment or null\n */\n async findByTracking(trackingNumber: string): Promise<Fulfillment | null> {\n const results = await this.list({\n where: { trackingNumber },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Find fulfillments shipped in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of fulfillments\n */\n async findShippedInRange(\n startDate: Date,\n endDate: Date,\n ): Promise<Fulfillment[]> {\n return await this.list({\n where: {\n 'shippedAt >=': startDate.toISOString(),\n 'shippedAt <=': endDate.toISOString(),\n },\n orderBy: 'shippedAt DESC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all fulfillments belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of fulfillments for the tenant\n */\n async findByTenant(tenantId: string): Promise<Fulfillment[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global fulfillments (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 fulfillments\n */\n async findGlobal(): Promise<Fulfillment[]> {\n return queryGlobal<Fulfillment>(this);\n }\n\n /**\n * Find fulfillments for a tenant including global fulfillments.\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 fulfillments\n */\n async findWithGlobals(tenantId: string): Promise<Fulfillment[]> {\n return queryWithGlobals<Fulfillment>(\n this,\n tenantId,\n 'Fulfillment.findWithGlobals',\n );\n }\n}\n","/**\n * FulfillmentLineItem model - tracks which contract items were fulfilled\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Sub-unit rounding tolerance for the over-fulfillment cap, matching the rest\n * of the package's EPSILON convention. Quantities are decimal, so a tiny\n * float-summation overshoot must not trip the guard.\n */\nconst FULFILLMENT_QUANTITY_EPSILON = 0.01;\n\n/**\n * FulfillmentLineItem tracks which contract line items are included\n * in a specific fulfillment and how much was fulfilled.\n *\n * This allows partial fulfillment - e.g., shipping 5 of 10 items ordered.\n *\n * @example\n * ```typescript\n * const fulfillmentItem = await fulfillmentItems.create({\n * fulfillmentId: fulfillment.id,\n * contractLineItemId: lineItem.id,\n * quantityFulfilled: 5 // of 10 ordered\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class FulfillmentLineItem extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global fulfillment line items\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent fulfillment\n */\n @foreignKey('Fulfillment')\n fulfillmentId: string = '';\n\n /**\n * Contract line item being fulfilled\n */\n @foreignKey('ContractLineItem')\n contractLineItemId: string = '';\n\n /**\n * Quantity fulfilled in this fulfillment\n */\n quantityFulfilled: number = 1.0;\n\n /**\n * Notes about this line item\n */\n notes: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.fulfillmentId !== undefined)\n this.fulfillmentId = options.fulfillmentId;\n if (options.contractLineItemId !== undefined)\n this.contractLineItemId = options.contractLineItemId;\n if (options.quantityFulfilled !== undefined)\n this.quantityFulfilled = options.quantityFulfilled;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Save-time over-fulfillment guard (S5 audit #1390 follow-up, round 2):\n * - `quantityFulfilled` must be a finite, positive number,\n * - the referenced `ContractLineItem` must resolve **within the caller's\n * tenant scope** AND belong to the same contract as the parent\n * Fulfillment, and\n * - the sum of all FulfillmentLineItems against that ContractLineItem\n * (this row included) must not exceed the ordered `ContractLineItem.quantity`.\n *\n * Without this, a caller could ship 50 of 10 ordered — the direct parallel\n * to the PaymentAllocation over-allocation hole #1390 closed.\n *\n * Tenant isolation (round 2): the ContractLineItem is loaded through\n * `ContractLineItemCollection`, so it goes through the tenancy\n * auto-filtering interceptors. A cross-tenant `contractLineItemId` therefore\n * fails closed — it resolves to `null` and trips the existing \"does not\n * exist\" error rather than leaking a foreign-tenant ordered quantity. The\n * raw `this.db.get('contract_line_items', …)` it replaced bypassed those\n * filters. We additionally load the parent Fulfillment (also tenant-scoped)\n * and reject when the line item belongs to a *different* contract than the\n * Fulfillment is fulfilling, so a valid-but-unrelated line id can't be\n * used to fulfill against the wrong order.\n */\n override async save(): Promise<this> {\n if (\n !Number.isFinite(this.quantityFulfilled) ||\n this.quantityFulfilled <= 0\n ) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: quantityFulfilled must be a ` +\n `positive number (got ${this.quantityFulfilled}).`,\n );\n }\n\n if (this.contractLineItemId) {\n const { ContractLineItemCollection } = await import(\n '../collections/ContractLineItemCollection.js'\n );\n const lineItems = await (ContractLineItemCollection as any).create(\n this.options,\n );\n const orderedLineItem = await lineItems.get(this.contractLineItemId);\n if (!orderedLineItem) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: referenced ContractLineItem ` +\n `'${this.contractLineItemId}' does not exist — refusing to fulfill ` +\n 'against a missing line item (the over-fulfillment cap cannot be ' +\n 'enforced otherwise).',\n );\n }\n\n // The line item must belong to the same contract the parent Fulfillment\n // is fulfilling. Otherwise a caller could point a Fulfillment for\n // contract A at a line item from contract B (over-fulfilling B's order\n // via A's shipment, and falsifying both contracts' fulfilled totals).\n if (this.fulfillmentId) {\n const { FulfillmentCollection } = await import(\n '../collections/FulfillmentCollection.js'\n );\n const fulfillments = await (FulfillmentCollection as any).create(\n this.options,\n );\n const fulfillment = await fulfillments.get(this.fulfillmentId);\n if (\n fulfillment &&\n orderedLineItem.contractId !== fulfillment.contractId\n ) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: ContractLineItem ` +\n `'${this.contractLineItemId}' belongs to contract ` +\n `'${orderedLineItem.contractId}', but its Fulfillment ` +\n `'${this.fulfillmentId}' fulfills contract ` +\n `'${fulfillment.contractId}' — refusing to fulfill a line item ` +\n 'from a different contract.',\n );\n }\n }\n\n const ordered = Number(orderedLineItem.quantity);\n\n const { FulfillmentLineItemCollection } = await import(\n '../collections/FulfillmentLineItemCollection.js'\n );\n const siblings = await (FulfillmentLineItemCollection as any).create(\n this.options,\n );\n const existing = await siblings.findByContractLineItem(\n this.contractLineItemId,\n );\n const otherFulfilled = existing.reduce(\n (sum: number, item: FulfillmentLineItem) =>\n item.id === this.id ? sum : sum + item.quantityFulfilled,\n 0,\n );\n\n if (\n Number.isFinite(ordered) &&\n otherFulfilled + this.quantityFulfilled - ordered >\n FULFILLMENT_QUANTITY_EPSILON\n ) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: fulfilling ` +\n `${this.quantityFulfilled} would over-fulfill contract line ` +\n `'${this.contractLineItemId}' — already fulfilled ${otherFulfilled} ` +\n `of ${ordered} ordered.`,\n );\n }\n }\n\n return super.save() as Promise<this>;\n }\n}\n\nexport default FulfillmentLineItem;\n","/**\n * FulfillmentLineItemCollection - Collection manager for FulfillmentLineItem objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { FulfillmentLineItem } from '../models/FulfillmentLineItem.js';\n\nexport class FulfillmentLineItemCollection extends SmrtCollection<FulfillmentLineItem> {\n static readonly _itemClass = FulfillmentLineItem;\n\n /**\n * Find fulfillment line items by their parent fulfillment.\n *\n * @param fulfillmentId - Fulfillment ID\n * @returns Array of fulfillment line items\n */\n async findByFulfillment(\n fulfillmentId: string,\n ): Promise<FulfillmentLineItem[]> {\n return await this.list({\n where: { fulfillmentId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find every fulfillment line item that fulfills a given contract line item,\n * across all fulfillments. Used by the over-fulfillment cap to sum how much\n * of an ordered line has already been fulfilled.\n *\n * @param contractLineItemId - Contract line item ID\n * @returns Array of fulfillment line items targeting that contract line\n */\n async findByContractLineItem(\n contractLineItemId: string,\n ): Promise<FulfillmentLineItem[]> {\n return await this.list({\n where: { contractLineItemId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Sum the quantity already fulfilled for a contract line item across all\n * fulfillments.\n *\n * @param contractLineItemId - Contract line item ID\n * @returns Total quantity fulfilled\n */\n async getTotalFulfilledForContractLine(\n contractLineItemId: string,\n ): Promise<number> {\n const items = await this.findByContractLineItem(contractLineItemId);\n return items.reduce((sum, item) => sum + item.quantityFulfilled, 0);\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all fulfillment line items belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of fulfillment line items for the tenant\n */\n async findByTenant(tenantId: string): Promise<FulfillmentLineItem[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global fulfillment 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 fulfillment line items\n */\n async findGlobal(): Promise<FulfillmentLineItem[]> {\n return queryGlobal<FulfillmentLineItem>(this);\n }\n\n /**\n * Find fulfillment 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 fulfillment line items\n */\n async findWithGlobals(tenantId: string): Promise<FulfillmentLineItem[]> {\n return queryWithGlobals<FulfillmentLineItem>(\n this,\n tenantId,\n 'FulfillmentLineItem.findWithGlobals',\n );\n }\n}\n","/**\n * Invoice model - billing document sent to customers\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { InvoiceStatus, type RecognizeRevenueOptions } from '../types/index.js';\n\n/**\n * Sub-cent rounding tolerance for the invoice's own integrity checks (forged\n * total / over-payment / payment-status consistency). Used when comparing\n * caller-supplied totals against recomputed line-item totals so floating-point\n * fuzz doesn't trip the guards.\n *\n * NOTE: this is intentionally looser than the smrt-ledgers `BALANCE_EPSILON`\n * (0.001). To keep a no-line-item invoice ledger-postable, {@link\n * Invoice.assertTotalArithmetic} SNAPS `totalAmount` to exactly\n * `subtotal + taxAmount` after the tolerance check, so every journal built in\n * {@link Invoice.recognizeRevenue} balances under the tighter ledger epsilon.\n */\nconst INVOICE_EPSILON = 0.01;\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 (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.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.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.PARTIAL,\n InvoiceStatus.VIEWED,\n InvoiceStatus.SENT,\n InvoiceStatus.OVERDUE,\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 '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: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Invoice extends SmrtObject {\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 @foreignKey('Customer')\n customerId: string = '';\n\n /**\n * Optional link to Contract (cross-package reference)\n */\n @foreignKey('Contract')\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 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 issueDate: Date = new Date();\n\n /**\n * Payment due date\n */\n dueDate: Date = new Date();\n\n /**\n * Date invoice was fully paid\n */\n paidDate: Date | null = null;\n\n // ============================================================================\n // Amounts\n // ============================================================================\n\n /**\n * Subtotal before tax\n */\n subtotal: number = 0;\n\n /**\n * Tax amount\n */\n taxAmount: number = 0;\n\n /**\n * Total amount due (subtotal + tax)\n */\n totalAmount: number = 0;\n\n /**\n * Amount paid (sum of PaymentAllocations)\n */\n amountPaid: number = 0;\n\n /**\n * Currency code (ISO 4217)\n */\n currency: string = 'CAD';\n\n // ============================================================================\n // Status\n // ============================================================================\n\n /**\n * Current invoice status\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 /**\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 // ============================================================================\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 notes: string = '';\n\n /**\n * Notes shown to customer on invoice\n */\n customerNotes: string = '';\n\n /**\n * Payment terms text\n */\n terms: string = '';\n\n constructor(options: any = {}) {\n super(options);\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.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.status !== undefined) this.status = options.status;\n if (options.arJournalId !== undefined)\n this.arJournalId = options.arJournalId;\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.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` (beyond rounding tolerance).\n * - **Non-negativity** is enforced on all four amounts.\n * - **Status transitions** are validated against the prior persisted status.\n */\n override async save(): Promise<this> {\n const persisted = await this.isSaved();\n const prior = await this.resolvePriorStatus(persisted);\n\n await this.recomputeAmountsForSave(persisted);\n this.assertNonNegativeAmounts();\n this.assertStatusTransition(prior);\n this.assertPaymentStatusConsistent();\n\n const result = (await super.save()) as this;\n // Once written, the current status becomes the new \"prior\" for the next\n // save in this instance's lifetime.\n loadedInvoiceStatus.set(this, this.status);\n return result;\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 (\n InvoiceLineItemCollection as any\n ).create(this.options);\n const lineItems = await lineItemCollection.findByInvoice(this.id);\n\n if (lineItems.length > 0) {\n const subtotal = lineItems.reduce(\n (sum: number, item: any) => sum + item.getSubtotal(),\n 0,\n );\n const taxAmount = lineItems.reduce(\n (sum: number, item: any) => sum + item.getTaxAmount(),\n 0,\n );\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 const { PaymentAllocationCollection } = await import(\n '../collections/PaymentAllocationCollection.js'\n );\n const allocationCollection = await (\n PaymentAllocationCollection as any\n ).create(this.options);\n const allocated = await allocationCollection.getTotalAllocatedToInvoice(\n this.id,\n );\n this.amountPaid = allocated;\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 /**\n * Whether `amountPaid` covers `totalAmount` within the sub-cent rounding\n * tolerance. This is the SINGLE source of truth for the **PAID** decision —\n * both {@link updatePaymentStatus} (which decides PAID) and\n * {@link assertPaymentStatusConsistent} (which validates PAID on save) call\n * it, so the PAID-deciding comparison and the PAID-validating comparison can\n * never drift apart. A strict `amountPaid >= totalAmount` here would diverge\n * from the epsilon-tolerant guard: a float-summed total paid exactly (e.g.\n * `0.1 × 3` line items paid `0.3`) reads as \"not covered\" by `>=` but\n * \"covered\" by the guard, so `updatePaymentStatus` would set PARTIAL and the\n * save-time guard would then reject it — leaving a genuinely-paid invoice\n * unsaveable (S5 audit #1390 follow-up).\n *\n * NOTE: the claim is scoped to PAID. The PARTIAL branch is NOT unified the\n * same way — {@link updatePaymentStatus} treats any `amountPaid > 0` as\n * PARTIAL, whereas {@link isPartiallyPaid} (used by the save-time guard)\n * requires `amountPaid > INVOICE_EPSILON`. That pre-existing asymmetry only\n * matters for a sub-cent dust payment and is intentionally left as-is.\n */\n private isFullyPaid(): boolean {\n return this.amountPaid - this.totalAmount >= -INVOICE_EPSILON;\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 > INVOICE_EPSILON && !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 open states SENT / VIEWED). Lifecycle statuses that aren't a\n * function of amountPaid — DRAFT, 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.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 > INVOICE_EPSILON &&\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 /**\n * Enforce `totalAmount === subtotal + taxAmount` (within rounding tolerance),\n * then SNAP `totalAmount` to the exact arithmetic. Used when the invoice has\n * no line items to recompute from.\n *\n * The snap closes the invoice-vs-ledger epsilon gap (S5 audit #1390\n * follow-up): the invoice guard tolerates `INVOICE_EPSILON` (0.01) but the\n * smrt-ledgers balance check uses a tighter `BALANCE_EPSILON` (0.001). A\n * no-line-item invoice with, say, subtotal 100.00 / total 100.005 passes this\n * guard, yet `recognizeRevenue` would build DR 100.005 / CR 100.00 and\n * `journal.post()` would reject it as unbalanced — voiding the journal and\n * permanently blocking revenue recognition. Snapping `totalAmount` to\n * `subtotal + taxAmount` here (after the tolerance check) means the persisted\n * total and every journal built from it are always ledger-consistent.\n */\n private assertTotalArithmetic(): void {\n const expected = this.subtotal + this.taxAmount;\n if (Math.abs(this.totalAmount - expected) > INVOICE_EPSILON) {\n throw new Error(\n `Invoice ${this.invoiceNumber || this.id || '<new>'}: totalAmount ${this.totalAmount} ` +\n `must equal subtotal + taxAmount (${expected}).`,\n );\n }\n this.totalAmount = expected;\n }\n\n /**\n * Reject negative financial values and an amountPaid that exceeds the total\n * (beyond rounding tolerance — overpayment is modelled elsewhere, not by\n * letting amountPaid float above the invoice total).\n */\n private assertNonNegativeAmounts(): void {\n const label = this.invoiceNumber || this.id || '<new>';\n for (const [field, value] of [\n ['subtotal', this.subtotal],\n ['taxAmount', this.taxAmount],\n ['totalAmount', this.totalAmount],\n ['amountPaid', this.amountPaid],\n ] as const) {\n if (!Number.isFinite(value) || 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 > INVOICE_EPSILON) {\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.totalAmount - this.amountPaid);\n }\n\n // ============================================================================\n // Status Transitions\n // ============================================================================\n\n /**\n * Mark invoice as sent\n */\n markSent(): void {\n if (this.status !== InvoiceStatus.DRAFT) {\n throw new Error(\n `Cannot send invoice with status '${this.status}'. Only draft 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 * (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 this.status = InvoiceStatus.PAID;\n this.paidDate = new Date();\n } else if (amountPaid > 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.sentAt) {\n this.status = this.viewedAt ? InvoiceStatus.VIEWED : InvoiceStatus.SENT;\n } else {\n this.status = InvoiceStatus.DRAFT;\n }\n }\n }\n\n /**\n * Cancel the invoice\n */\n cancel(): void {\n if (this.isPaid()) {\n throw new Error(\n `Cannot cancel invoice with status '${this.status}'. Paid invoices cannot be cancelled.`,\n );\n }\n this.status = InvoiceStatus.CANCELLED;\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<any> {\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 const persisted = await this.isSaved();\n await this.recomputeAmountsForSave(persisted);\n this.assertNonNegativeAmounts();\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 as any).create(\n this.options,\n );\n\n // Build entries array from the now-authoritative totals.\n const entries: any[] = [\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 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<any | null> {\n if (!this.arJournalId) return null;\n\n try {\n const { JournalCollection } = await import('@happyvertical/smrt-ledgers');\n const collection = await (JournalCollection as any).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.\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(): Promise<any> {\n // Dynamically import to avoid circular dependency\n const { InvoiceLineItemCollection } = await import(\n '../collections/InvoiceLineItemCollection.js'\n );\n\n const lineItemCollection = await (InvoiceLineItemCollection as any).create(\n this.options,\n );\n const lineItems = await lineItemCollection.findByInvoice(this.id);\n\n return {\n id: this.id,\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: any) => item.toAccountingLineItem()),\n subtotal: this.subtotal,\n taxAmount: this.taxAmount,\n totalAmount: this.totalAmount,\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 { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Invoice } from '../models/Invoice.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 /**\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","/**\n * InvoiceLineItem model - individual items on an invoice\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\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: 0.01, // per impression\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: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class InvoiceLineItem extends SmrtObject {\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 @foreignKey('Invoice')\n invoiceId: string = '';\n\n /**\n * Item description\n */\n description: string = '';\n\n /**\n * SKU or item code\n */\n sku: string = '';\n\n /**\n * Quantity (e.g., impressions, hours, units)\n */\n quantity: number = 1;\n\n /**\n * Unit price before discount\n */\n unitPrice: number = 0;\n\n /**\n * Discount amount (flat, not percentage)\n */\n discount: number = 0;\n\n /**\n * Tax rate as decimal (e.g., 0.05 for 5%)\n */\n taxRate: number = 0;\n\n /**\n * Calculated line amount\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')\n revenueAccountId: string = '';\n\n /**\n * Sort order within the invoice\n */\n sortOrder: number = 0;\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.invoiceId !== undefined) this.invoiceId = options.invoiceId;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.sku !== undefined) this.sku = options.sku;\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.taxRate !== undefined) this.taxRate = options.taxRate;\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 /**\n * Calculate the line amount.\n *\n * Formula: (quantity * unitPrice - discount) * (1 + taxRate)\n *\n * Tax is calculated on the discounted subtotal. This follows the common\n * \"discount before tax\" approach used in most North American jurisdictions.\n * For jurisdictions requiring different tax calculation methods, override\n * this method or calculate amounts externally.\n */\n calculateAmount(): number {\n const subtotal = this.quantity * this.unitPrice - this.discount;\n const tax = subtotal * this.taxRate;\n return subtotal + tax;\n }\n\n /**\n * Get subtotal (before tax)\n */\n getSubtotal(): number {\n return this.quantity * this.unitPrice - this.discount;\n }\n\n /**\n * Get tax amount\n */\n getTaxAmount(): number {\n return this.getSubtotal() * this.taxRate;\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 toAccountingLineItem(): any {\n return {\n description: this.description,\n sku: this.sku || undefined,\n quantity: this.quantity,\n unitPrice: this.unitPrice,\n discount: this.discount || undefined,\n taxRate: this.taxRate || undefined,\n amount: 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 { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { InvoiceLineItem } from '../models/InvoiceLineItem.js';\n\nexport class InvoiceLineItemCollection extends SmrtCollection<InvoiceLineItem> {\n static readonly _itemClass = InvoiceLineItem;\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\n */\n async toAccountingLineItems(invoiceId: string): Promise<any[]> {\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","/**\n * PaymentAllocation model - maps payments to invoices\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\n/**\n * Sub-cent rounding tolerance for over-allocation checks, matching the rest\n * of the package's EPSILON convention.\n */\nconst ALLOCATION_EPSILON = 0.01;\n\n/**\n * PaymentAllocation tracks how payments are applied to invoices.\n *\n * This enables:\n * - Partial payments (one payment partially covering an invoice)\n * - Split payments (one payment split across multiple invoices)\n * - Payment history per invoice\n *\n * **Note**: This model does not automatically validate that allocations\n * don't exceed the payment amount or invoice amount due. Use\n * `PaymentAllocationCollection.getUnallocatedFromPayment()` before creating\n * allocations to ensure sufficient funds are available.\n *\n * @example\n * ```typescript\n * import { PaymentAllocationCollection } from '@happyvertical/smrt-commerce';\n *\n * // Get the collection\n * const allocations = await PaymentAllocationCollection.create(options);\n *\n * // Check available funds before allocating\n * const available = await allocations.getUnallocatedFromPayment(\n * payment.id,\n * payment.amount\n * );\n *\n * if (available >= amountToAllocate) {\n * const allocation = await allocations.create({\n * paymentId: payment.id,\n * invoiceId: invoice.id,\n * amount: amountToAllocate,\n * allocatedBy: 'user-uuid'\n * });\n *\n * // Update invoice payment status\n * const totalAllocated = await allocations.getTotalAllocatedToInvoice(invoice.id);\n * invoice.updatePaymentStatus(totalAllocated);\n * await invoice.save();\n * }\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // NOTE: `create` and `delete` are intentionally NOT exposed over the\n // generated REST/MCP surface (S5 audit #1390). Allocation rows directly\n // determine an invoice's amountPaid/status and a payment's remaining funds;\n // a generated `create` lets a caller forge an allocation that over-applies a\n // payment, and a generated `delete` lets a caller silently un-apply funds —\n // both falsify balances with no authz. Allocations must be created/removed\n // through application code that re-derives invoice status and respects the\n // payment-amount cap (the cap is also enforced in `save()` as defence in\n // depth).\n //\n // CLI parity (round 6, codex ROOT INSIGHT): the CLI is an independently\n // configured write surface. `cli: true` would still generate create/update/\n // delete commands that forge or un-apply allocations, re-opening the exact\n // vector closed on api/mcp. The CLI is locked to the same read-only surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class PaymentAllocation extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global payment allocations\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Payment being allocated\n */\n @foreignKey('Payment')\n paymentId: string = '';\n\n /**\n * Invoice receiving the allocation\n */\n @foreignKey('Invoice')\n invoiceId: string = '';\n\n /**\n * Amount allocated from payment to invoice\n */\n amount: number = 0;\n\n /**\n * When the allocation was made\n */\n allocatedAt: Date = new Date();\n\n /**\n * User/agent who made the allocation\n */\n allocatedBy: string = '';\n\n /**\n * Notes about the allocation\n */\n notes: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.invoiceId !== undefined) this.invoiceId = options.invoiceId;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.allocatedAt !== undefined)\n this.allocatedAt = options.allocatedAt;\n if (options.allocatedBy !== undefined)\n this.allocatedBy = options.allocatedBy;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Save-time integrity guard (S5 audit #1390 + follow-up):\n * - allocation `amount` must be a finite, positive number,\n * - the sum of all allocations against the referenced Payment (this row\n * included) must not exceed the Payment's amount — over-applying a\n * payment across invoices would falsify both payment and invoice\n * balances, and\n * - the sum of all allocations against the referenced Invoice (this row\n * included) must not exceed the Invoice's `totalAmount`.\n *\n * The Payment-amount cap is enforced against the persisted Payment row. An\n * allocation always carries a `@foreignKey('Payment')` paymentId, so a\n * `paymentId` that doesn't resolve to a real Payment is a **hard error** (S5\n * audit #1390 round 2): previously a missing Payment silently skipped the cap\n * entirely, letting a caller over-apply (or fabricate) funds simply by\n * pointing at a non-existent payment. An empty `paymentId` is still rejected\n * by the underlying FK requirement; the positivity check always applies.\n *\n * The Invoice-total cap (follow-up) closes a complementary hole: the\n * per-Payment cap lets allocations from *different* payments each pass their\n * own check while jointly summing above the invoice total. The next\n * `Invoice.save()` would then recompute `amountPaid` from these allocations,\n * trip `assertNonNegativeAmounts` (amountPaid > totalAmount), and leave the\n * invoice permanently unsaveable while the over-allocations persist. Capping\n * here keeps allocations from ever exceeding what the invoice owes. The cap\n * is skipped only when the invoice row can't be resolved (e.g. ledger-less /\n * not-yet-persisted) so it never blocks an otherwise-valid allocation.\n */\n override async save(): Promise<this> {\n if (!Number.isFinite(this.amount) || this.amount <= 0) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: amount must be a positive number (got ${this.amount}).`,\n );\n }\n\n if (this.paymentId) {\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await (PaymentCollection as any).create(this.options);\n const payment = await payments.get({ id: this.paymentId });\n if (!payment) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: referenced Payment ` +\n `'${this.paymentId}' does not exist — refusing to allocate against a ` +\n 'missing payment (the payment-amount cap cannot be enforced otherwise).',\n );\n }\n const { PaymentAllocationCollection } = await import(\n '../collections/PaymentAllocationCollection.js'\n );\n const allocations = await (PaymentAllocationCollection as any).create(\n this.options,\n );\n const existing = await allocations.findByPayment(this.paymentId);\n const otherTotal = existing.reduce(\n (sum: number, alloc: PaymentAllocation) =>\n alloc.id === this.id ? sum : sum + alloc.amount,\n 0,\n );\n if (otherTotal + this.amount - payment.amount > ALLOCATION_EPSILON) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: allocating ${this.amount} would over-apply ` +\n `payment '${this.paymentId}' — already allocated ${otherTotal} of ${payment.amount}.`,\n );\n }\n }\n\n if (this.invoiceId) {\n const { InvoiceCollection } = await import(\n '../collections/InvoiceCollection.js'\n );\n const invoices = await (InvoiceCollection as any).create(this.options);\n const invoice = await invoices.get({ id: this.invoiceId });\n // Only enforce when the invoice resolves and carries a real positive\n // total — a missing/zero-total invoice (not yet persisted, ledger-less\n // test fixture) skips the cap rather than blocking a valid allocation.\n // `Number.isFinite(0)` is `true`, so the prior `isFinite` guard wrongly\n // capped a freshly-created total=0 invoice at 0 and rejected every\n // allocation against it; gate on `> ALLOCATION_EPSILON` to match the\n // documented \"zero/missing total skips the cap\" intent.\n if (invoice && invoice.totalAmount > ALLOCATION_EPSILON) {\n const { PaymentAllocationCollection } = await import(\n '../collections/PaymentAllocationCollection.js'\n );\n const allocations = await (PaymentAllocationCollection as any).create(\n this.options,\n );\n const existingForInvoice = await allocations.findByInvoice(\n this.invoiceId,\n );\n const otherInvoiceTotal = existingForInvoice.reduce(\n (sum: number, alloc: PaymentAllocation) =>\n alloc.id === this.id ? sum : sum + alloc.amount,\n 0,\n );\n if (\n otherInvoiceTotal + this.amount - invoice.totalAmount >\n ALLOCATION_EPSILON\n ) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: allocating ${this.amount} would over-pay ` +\n `invoice '${this.invoiceId}' — already allocated ${otherInvoiceTotal} of ` +\n `${invoice.totalAmount}.`,\n );\n }\n }\n }\n\n return super.save() as Promise<this>;\n }\n}\n\nexport default PaymentAllocation;\n","/**\n * PaymentAllocationCollection - Collection manager for PaymentAllocation objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { PaymentAllocation } from '../models/PaymentAllocation.js';\n\nexport class PaymentAllocationCollection extends SmrtCollection<PaymentAllocation> {\n static readonly _itemClass = PaymentAllocation;\n\n /**\n * Find allocations by payment\n *\n * @param paymentId - Payment ID\n * @returns Array of allocations\n */\n async findByPayment(paymentId: string): Promise<PaymentAllocation[]> {\n return await this.list({\n where: { paymentId },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n /**\n * Find allocations by invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Array of allocations\n */\n async findByInvoice(invoiceId: string): Promise<PaymentAllocation[]> {\n return await this.list({\n where: { invoiceId },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n /**\n * Get total amount allocated to an invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Total allocated amount\n */\n async getTotalAllocatedToInvoice(invoiceId: string): Promise<number> {\n const allocations = await this.findByInvoice(invoiceId);\n return allocations.reduce((sum, alloc) => sum + alloc.amount, 0);\n }\n\n /**\n * Get total amount allocated from a payment\n *\n * @param paymentId - Payment ID\n * @returns Total allocated amount\n */\n async getTotalAllocatedFromPayment(paymentId: string): Promise<number> {\n const allocations = await this.findByPayment(paymentId);\n return allocations.reduce((sum, alloc) => sum + alloc.amount, 0);\n }\n\n /**\n * Get remaining unallocated amount from a payment.\n *\n * @param paymentId - Payment ID\n * @param paymentAmount - Total payment amount\n * @returns Unallocated amount\n * @throws Error if payment is over-allocated (indicates data integrity issue)\n */\n async getUnallocatedFromPayment(\n paymentId: string,\n paymentAmount: number,\n ): Promise<number> {\n const allocated = await this.getTotalAllocatedFromPayment(paymentId);\n const remaining = paymentAmount - allocated;\n\n if (remaining < 0) {\n throw new Error(\n `Over-allocated payment detected for paymentId=${paymentId}: ` +\n `allocated amount (${allocated}) exceeds payment amount (${paymentAmount})`,\n );\n }\n\n return remaining;\n }\n\n /**\n * Find allocation by payment and invoice\n *\n * @param paymentId - Payment ID\n * @param invoiceId - Invoice ID\n * @returns Allocation or null\n */\n async findByPaymentAndInvoice(\n paymentId: string,\n invoiceId: string,\n ): Promise<PaymentAllocation | null> {\n const results = await this.list({\n where: { paymentId, invoiceId },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Find allocations by allocator\n *\n * @param allocatedBy - User/agent ID who made the allocation\n * @returns Array of allocations\n */\n async findByAllocator(allocatedBy: string): Promise<PaymentAllocation[]> {\n return await this.list({\n where: { allocatedBy },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n /**\n * Find allocations in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of allocations\n */\n async findByDateRange(\n startDate: Date,\n endDate: Date,\n ): Promise<PaymentAllocation[]> {\n return await this.list({\n where: {\n 'allocatedAt >=': startDate.toISOString(),\n 'allocatedAt <=': endDate.toISOString(),\n },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all payment allocations belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of payment allocations for the tenant\n */\n async findByTenant(tenantId: string): Promise<PaymentAllocation[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payment allocations (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 payment allocations\n */\n async findGlobal(): Promise<PaymentAllocation[]> {\n return queryGlobal<PaymentAllocation>(this);\n }\n\n /**\n * Find payment allocations for a tenant including global allocations.\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 payment allocations\n */\n async findWithGlobals(tenantId: string): Promise<PaymentAllocation[]> {\n return queryWithGlobals<PaymentAllocation>(\n this,\n tenantId,\n 'PaymentAllocation.findWithGlobals',\n );\n }\n}\n","/**\n * Payment model - tracks payments with ledger integration\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n PaymentMethod,\n PaymentStatus,\n type RecordPaymentOptions,\n} from '../types/index.js';\n\n/**\n * Legal status transitions for a Payment, keyed by the prior persisted status.\n * A status mapping to itself (no-op re-save) is always permitted and handled\n * separately. Transitioning *into* COMPLETED is additionally gated on the\n * verified `recordPayment()` settlement path (see {@link Payment.save}) — it is\n * never reachable by raw mass-assignment even though it appears here as a\n * structurally legal edge out of PENDING.\n *\n * Forward path: PENDING → COMPLETED, with FAILED / CANCELLED as alternate\n * terminal exits from PENDING and REFUNDED reachable from COMPLETED.\n */\nconst PAYMENT_STATUS_TRANSITIONS: Record<PaymentStatus, PaymentStatus[]> = {\n [PaymentStatus.PENDING]: [\n PaymentStatus.COMPLETED,\n PaymentStatus.FAILED,\n PaymentStatus.CANCELLED,\n ],\n // A completed payment can only be reversed via refund.\n [PaymentStatus.COMPLETED]: [PaymentStatus.REFUNDED],\n // Terminal states.\n [PaymentStatus.FAILED]: [],\n [PaymentStatus.REFUNDED]: [],\n [PaymentStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each Payment instance was loaded with,\n * so the save-time transition guard can compare the prior persisted status\n * against the one being written without adding a persisted column. WeakMap\n * keeps it out of the schema and GCs with the instance — same pattern as the\n * other commerce models.\n */\nconst loadedPaymentStatus = new WeakMap<Payment, PaymentStatus>();\n\n/**\n * Marks instances whose transition into COMPLETED is being driven by the\n * verified `recordPayment()` settlement path (which posts a balanced journal\n * before flipping the status). The save-time guard consults this set so that\n * COMPLETED can only be reached through that path, never via raw\n * mass-assignment on the generated update route. Cleared after the save runs.\n */\nconst settlementInProgress = new WeakSet<Payment>();\n\n/**\n * Payment represents a financial transaction against a contract.\n *\n * Payments can be integrated with smrt-ledgers to automatically\n * create balanced journal entries for proper accounting.\n *\n * @example\n * ```typescript\n * // Create a payment\n * const payment = await payments.create({\n * contractId: order.id,\n * customerId: customer.id,\n * amount: 1500.00,\n * method: PaymentMethod.CREDIT_CARD,\n * transactionId: 'stripe_pi_123456'\n * });\n *\n * // Record with ledger integration\n * await payment.recordPayment({\n * ledgerId: ledger.id,\n * receivablesAccountId: arAccount.id,\n * cashAccountId: bankAccount.id\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // ROOT FIX (S5 audit #1390 round 4): restrict the generated create/update\n // write surface so privileged / settlement-derived fields can NEVER be set\n // by a generated REST/MCP route — closing the status-mass-assignment vector\n // at the surface rather than relying solely on the save()-time guard.\n //\n // `writable` is an allowlist: only these fields survive applyWritablePolicy()\n // on BOTH create and update (#1540). Deliberately EXCLUDED:\n // - `status` — a COMPLETED Payment is settlement proof downstream\n // (PaymentIntent's PAID verification trusts it). It is only ever set by\n // the verified `recordPayment()` settlement path, never by a caller. This\n // closes the \"forged COMPLETED Payment via api.create\" vector (codex HIGH#1).\n // - `journalId` / `paidAt` — settlement-derived, written by recordPayment().\n // - `syncedAt` — provider-sync bookkeeping, written by the sync path.\n api: {\n include: ['list', 'get', 'create', 'update'],\n writable: [\n 'contractId',\n 'customerId',\n 'amount',\n 'currency',\n 'method',\n 'transactionId',\n 'reference',\n 'notes',\n 'externalId',\n 'externalProvider',\n 'backendId',\n 'backendTxRef',\n 'nativeAmount',\n 'nativeCurrency',\n 'usdAtQuote',\n 'usdAtConfirmation',\n ],\n },\n // NOTE: `recordPayment` is intentionally NOT exposed over MCP. It posts a\n // balanced journal into smrt-ledgers (moves money in the books) and flips\n // the payment to COMPLETED — not safe as an unguarded MCP tool that carries\n // no authz. Financial mutations must go through application code that\n // enforces permissions. The MCP create/update tools honor the same\n // `api.writable` allowlist above, so `status` is unsettable over MCP too.\n // See S5 audit #1390.\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Payment extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global payments\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Contract this payment is for\n */\n @foreignKey('Contract')\n contractId: string = '';\n\n /**\n * Customer who made the payment\n */\n @foreignKey('Customer')\n customerId: string = '';\n\n /**\n * Payment amount\n */\n amount: number = 0.0;\n\n /**\n * Currency code (ISO 4217)\n */\n currency: string = 'USD';\n\n /**\n * Payment method used\n */\n method: PaymentMethod = PaymentMethod.BANK_TRANSFER;\n\n /**\n * Current payment status\n */\n status: PaymentStatus = PaymentStatus.PENDING;\n\n /**\n * External transaction ID (from payment processor)\n */\n transactionId: string = '';\n\n /**\n * Internal reference number\n */\n reference: string = '';\n\n /**\n * Link to smrt-ledgers Journal (created by recordPayment)\n */\n @crossPackageRef('@happyvertical/smrt-ledgers:Journal')\n journalId: string = '';\n\n /**\n * When the payment was completed\n */\n paidAt: Date | null = null;\n\n /**\n * Notes about the payment\n */\n notes: string = '';\n\n // ============================================================================\n // Provider Sync\n // ============================================================================\n\n /**\n * External ID in accounting provider (e.g., QBO payment ID)\n */\n externalId: string = '';\n\n /**\n * Accounting provider name ('quickbooks' | 'stripe' | 'paypal' | etc.)\n */\n externalProvider: string = '';\n\n /**\n * When payment was last synced to provider\n */\n syncedAt: Date | null = null;\n\n // ============================================================================\n // Payment Backend Identity\n // ============================================================================\n //\n // When a `PaymentBackend` adapter (Stripe, x402, BTC RPC, etc.) brokers the\n // payment rather than a human recording it manually, we capture the\n // backend identity, its native-currency amount, and the USD valuation at\n // both quote and confirmation time. The classic `amount` / `currency`\n // pair stays as the canonical settlement number; the backend fields\n // describe *how* the funds arrived and let downstream accounting close\n // the loop on volatile-currency drift.\n //\n // All fields default to empty / zero so existing Payment consumers that\n // pre-date the marketplace adapter machinery are unaffected.\n\n /**\n * Stable identifier of the `PaymentBackend` adapter that served this\n * payment — e.g. `base-usdc`, `solana-usdc`, `btc`, `stripe`, `paypal`.\n *\n * Distinct from {@link externalProvider}: `backendId` names the SMRT\n * payment-rail adapter, while `externalProvider` names a downstream\n * accounting destination (QuickBooks, Stripe-the-accounting-source).\n * The same Stripe payment will have `backendId: 'stripe'` AND\n * `externalProvider: 'stripe'`; a crypto payment synced to QBO will\n * have `backendId: 'base-usdc'` and `externalProvider: 'quickbooks'`.\n */\n backendId: string = '';\n\n /**\n * Chain transaction hash or backend aggregator's reference id. For\n * on-chain payments this is the tx hash (`0x...` on EVM, base58 sig on\n * Solana, txid on Bitcoin); for fiat-rail backends it's the gateway's\n * own reference. Kept distinct from {@link transactionId} so consumers\n * that already populate `transactionId` with a provider-internal id\n * don't have to overload it.\n */\n backendTxRef: string = '';\n\n /**\n * The amount the backend actually moved, in its own native currency.\n * For stablecoin rails this typically equals `amount`; for volatile-\n * currency rails (BTC, ETH) it's the satoshi/wei figure that the chain\n * recorded, independent of any USD valuation.\n */\n nativeAmount: number = 0.0;\n\n /**\n * Code identifying the native currency `nativeAmount` is denominated\n * in. Mirrors the `backendId` namespacing convention — `USDC-base`,\n * `BTC`, `ETH`, `USD-stripe`. Empty string means \"use {@link currency}\"\n * (i.e. the payment was already quoted and settled in the same\n * currency).\n */\n nativeCurrency: string = '';\n\n /**\n * USD valuation of the payment at the moment the price was quoted to\n * the buyer (typically the moment a `PaymentIntent` was issued).\n * Stored at decimal precision; empty default `0.0` means no USD-quote\n * snapshot was taken (the payment was already USD-denominated or the\n * backend doesn't require drift accounting).\n */\n usdAtQuote: number = 0.0;\n\n /**\n * USD valuation of the payment at the moment it was confirmed on the\n * backend (chain confirmation, gateway settlement, etc.). The delta\n * between {@link usdAtQuote} and `usdAtConfirmation` is the USD drift\n * the operator absorbs (positive or negative) when accepting payment\n * in a volatile native currency.\n */\n usdAtConfirmation: number = 0.0;\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.customerId !== undefined) this.customerId = options.customerId;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.method !== undefined) this.method = options.method;\n if (options.status !== undefined) this.status = options.status;\n if (options.transactionId !== undefined)\n this.transactionId = options.transactionId;\n if (options.reference !== undefined) this.reference = options.reference;\n if (options.journalId !== undefined) this.journalId = options.journalId;\n if (options.paidAt !== undefined) this.paidAt = options.paidAt;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.externalId !== undefined) this.externalId = options.externalId;\n if (options.externalProvider !== undefined)\n this.externalProvider = options.externalProvider;\n if (options.syncedAt !== undefined) this.syncedAt = options.syncedAt;\n if (options.backendId !== undefined) this.backendId = options.backendId;\n if (options.backendTxRef !== undefined)\n this.backendTxRef = options.backendTxRef;\n if (options.nativeAmount !== undefined)\n this.nativeAmount = options.nativeAmount;\n if (options.nativeCurrency !== undefined)\n this.nativeCurrency = options.nativeCurrency;\n if (options.usdAtQuote !== undefined) this.usdAtQuote = options.usdAtQuote;\n if (options.usdAtConfirmation !== undefined)\n this.usdAtConfirmation = options.usdAtConfirmation;\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 the generated update route, a stale caller,\n * etc.). Freshly-constructed (not-yet-saved) payments have no prior status.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedPaymentStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Save-time state-machine guard (S5 audit #1390).\n *\n * `status` is mass-assignable on the generated update/create routes, and a\n * COMPLETED Payment is treated as settlement proof downstream — e.g.\n * {@link PaymentIntent}'s PAID verification trusts a COMPLETED Payment row.\n * A forged `status: 'completed'` (with arbitrary amounts and no journal)\n * would therefore satisfy that check without any money having moved.\n *\n * This guard enforces two things:\n * - **Transitions must be legal** per {@link PAYMENT_STATUS_TRANSITIONS}.\n * - **Reaching COMPLETED requires the verified settlement path — on\n * creation AND on update.** Only `recordPayment()` (which posts a\n * balanced journal and links `journalId`) may write a Payment into\n * COMPLETED; it announces itself via {@link settlementInProgress}. A raw\n * `status: 'completed'` is rejected whether the row is brand-new (a\n * GENESIS `create({ status: 'completed' })`) or already persisted — that\n * is the exact path a forged COMPLETED would take to satisfy\n * PaymentIntent's PAID verification without any money having moved (codex\n * HIGH#1, #1390 round 5). There is deliberately NO import/fixture\n * carve-out: a COMPLETED Payment is settlement proof downstream, so it is\n * only ever reachable through `recordPayment()`, on every surface\n * (REST/MCP/CLI/direct). Fixtures/migrations that need a completed payment\n * must drive it through `recordPayment()` (or start non-COMPLETED).\n */\n override async save(): Promise<this> {\n const priorRow = await this.loadPersistedRow();\n const prior =\n priorRow && priorRow.status != null\n ? (priorRow.status as PaymentStatus)\n : loadedPaymentStatus.get(this);\n this.assertStatusTransition(prior);\n\n // Reaching COMPLETED is only legal via the verified settlement path,\n // regardless of whether this is a create (no persisted prior) or an\n // update of an existing row. A GENESIS create with status: COMPLETED would\n // otherwise forge settlement proof, so we close it here at the model level\n // — this covers EVERY surface (REST/MCP/CLI/direct), independent of any\n // writable-allowlist gap on a given surface.\n const reachingCompleted =\n this.status === PaymentStatus.COMPLETED &&\n prior !== PaymentStatus.COMPLETED;\n if (reachingCompleted && !settlementInProgress.has(this)) {\n throw new Error(\n `Payment ${this.id || '<new>'}: cannot set status to COMPLETED via ` +\n 'raw assignment — a Payment is only completed through ' +\n 'recordPayment(), which posts a balanced settlement journal. Use ' +\n 'recordPayment() instead of setting status directly.',\n );\n }\n\n // Freeze the settled monetary fields once the row is already COMPLETED in\n // the database (S5 audit #1390 round 6 — codex ROOT INSIGHT; mirrors the\n // PaymentIntent PAID backing-field freeze). A COMPLETED Payment is\n // settlement proof: its amount was reconciled against a balanced journal\n // and is trusted downstream — PaymentIntent's PAID verification binds the\n // winning option's `nativeAmount` to `Payment.amount` / `nativeAmount`. The\n // only legal exit from COMPLETED is COMPLETED → REFUNDED, and a refund must\n // NOT alter the settled figures. Closing this at the model level covers\n // EVERY surface (REST/MCP/CLI/direct), independent of the per-surface\n // writable allowlist (`amount`/`currency`/`nativeAmount`/... are writable\n // on a *new* PENDING payment, but must be immutable once settled).\n if (priorRow && priorRow.status === PaymentStatus.COMPLETED) {\n this.assertSettledAmountsUnchanged(priorRow);\n }\n\n try {\n const result = (await super.save()) as this;\n loadedPaymentStatus.set(this, this.status);\n return result;\n } finally {\n settlementInProgress.delete(this);\n }\n }\n\n /**\n * Reject an illegal status flip. Compares the about-to-be-written status\n * against the status the row was loaded with. No-op transitions (status\n * unchanged) and brand-new rows (no prior) are always allowed — the\n * COMPLETED-specific settlement requirement is enforced separately in\n * {@link save}.\n */\n private assertStatusTransition(prior: PaymentStatus | undefined): void {\n if (prior === undefined) return; // new row — any starting status (subject to the COMPLETED gate)\n if (prior === this.status) return; // no-op re-save\n const allowed = PAYMENT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Payment ${this.id}: illegal status transition '${prior}' → '${this.status}'. ` +\n 'Use the guarded helpers (recordPayment / markFailed / cancel).',\n );\n }\n }\n\n /**\n * Load the AUTHORITATIVE persisted row (S5 audit #1390 round 4, codex HIGH#1;\n * extended round 6 to carry the full row, not just `status`). The WeakMap is\n * only populated when {@link initialize} loaded the row from the DB; it is\n * empty for an instance built via\n * `collection.create({ id: <existing>, _skipLoad: true })` — the upsert path\n * that lets a caller write onto an existing row without hydrating it. Trusting\n * an empty WeakMap there would treat the write as a brand-new row and skip the\n * transition guard entirely (a poisonable prior-state).\n *\n * So when this instance carries an `id`, read the persisted row straight from\n * the database and use it as the prior — a create-onto-existing is an update.\n * Returns `undefined` when no row exists (truly new), so callers fall back to\n * the WeakMap (which is also empty then). Settled-amount columns are\n * snake_case (`native_amount`, etc.); the `status` column is single-word.\n */\n private async loadPersistedRow(): Promise<Record<string, any> | undefined> {\n if (!this.id) return undefined;\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n return row ?? undefined;\n } catch {\n // DB not ready / table absent — treat as new (in-memory fallback in save).\n return undefined;\n }\n }\n\n /**\n * Reject any change to the settled monetary fields of an already-COMPLETED\n * Payment (S5 audit #1390 round 6 — codex ROOT INSIGHT). Mirrors the\n * PaymentIntent PAID backing-field freeze. A COMPLETED Payment's economic\n * identity is settled: the `amount` was posted into a balanced journal and is\n * the figure PaymentIntent's PAID verification reconciles against, so it (and\n * the native/USD valuation fields that describe the same money) must not\n * drift. The only legal status move out of COMPLETED is REFUNDED, which\n * reverses the funds without rewriting how much they were. Compared against\n * the AUTHORITATIVE persisted row so the freeze holds on every surface, even\n * for an un-hydrated create-onto-existing upsert.\n */\n private assertSettledAmountsUnchanged(priorRow: Record<string, any>): void {\n const frozen: Array<[string, number | string, number | string]> = [\n ['amount', Number(priorRow.amount ?? 0), Number(this.amount ?? 0)],\n [\n 'currency',\n String(priorRow.currency ?? ''),\n String(this.currency ?? ''),\n ],\n [\n 'nativeAmount',\n Number(priorRow.native_amount ?? 0),\n Number(this.nativeAmount ?? 0),\n ],\n [\n 'nativeCurrency',\n String(priorRow.native_currency ?? ''),\n String(this.nativeCurrency ?? ''),\n ],\n [\n 'usdAtQuote',\n Number(priorRow.usd_at_quote ?? 0),\n Number(this.usdAtQuote ?? 0),\n ],\n [\n 'usdAtConfirmation',\n Number(priorRow.usd_at_confirmation ?? 0),\n Number(this.usdAtConfirmation ?? 0),\n ],\n ];\n for (const [field, prior, next] of frozen) {\n if (prior !== next) {\n throw new Error(\n `Payment ${this.id}: '${field}' is frozen once the payment is ` +\n `COMPLETED (was '${prior}', got '${next}'). A settled payment's ` +\n 'monetary fields cannot be mutated — issue a refund instead.',\n );\n }\n }\n }\n\n /**\n * USD drift between quote time and confirmation time — what the\n * operator gained (positive) or lost (negative) by accepting a\n * volatile-currency payment. Returns `0` when either side of the\n * comparison is missing or zero, so callers don't have to special-case\n * fiat-rail / stablecoin payments.\n */\n usdDrift(): number {\n if (!this.usdAtQuote || !this.usdAtConfirmation) return 0;\n return this.usdAtConfirmation - this.usdAtQuote;\n }\n\n /**\n * Check if payment is pending\n */\n isPending(): boolean {\n return this.status === PaymentStatus.PENDING;\n }\n\n /**\n * Check if payment is completed\n */\n isCompleted(): boolean {\n return this.status === PaymentStatus.COMPLETED;\n }\n\n /**\n * Check if payment is refunded\n */\n isRefunded(): boolean {\n return this.status === PaymentStatus.REFUNDED;\n }\n\n /**\n * Record the payment and create a balanced journal entry.\n *\n * Creates a journal entry in smrt-ledgers:\n * - Debit: Cash/Bank account (assets increase)\n * - Credit: Accounts Receivable (receivables decrease)\n *\n * @param options - Ledger and account configuration\n * @returns The created journal\n *\n * @example\n * ```typescript\n * const journal = await payment.recordPayment({\n * ledgerId: ledger.id,\n * receivablesAccountId: arAccount.id,\n * cashAccountId: bankAccount.id\n * });\n * console.log(`Created journal: ${journal.number}`);\n * ```\n */\n async recordPayment(options: RecordPaymentOptions): Promise<any> {\n if (this.status === PaymentStatus.COMPLETED) {\n throw new Error('Payment already recorded');\n }\n\n if (this.amount <= 0) {\n throw new Error('Payment amount must be positive');\n }\n\n // Dynamic import to avoid hard dependency on smrt-ledgers\n const { JournalCollection } = await import('@happyvertical/smrt-ledgers');\n\n // Create the journal collection\n const journalCollection = await (JournalCollection as any).create(\n this.options,\n );\n\n // Create a new journal for this payment\n const journal = await journalCollection.create({\n date: new Date(),\n description: `Payment received for contract ${this.contractId}`,\n sourceModule: 'smrt-commerce',\n sourceRef: this.id,\n });\n await journal.save();\n\n // Add balanced entries:\n // Debit Cash/Bank (assets increase)\n await journal.addEntry({\n accountId: options.cashAccountId,\n debit: this.amount,\n memo: `Payment ${this.reference || this.id}`,\n });\n\n // Credit Accounts Receivable (receivables decrease)\n await journal.addEntry({\n accountId: options.receivablesAccountId,\n credit: this.amount,\n memo: `Payment ${this.reference || this.id}`,\n });\n\n // Post the journal (validates balance and finalizes)\n await journal.post();\n\n // Update payment record. Announce the verified settlement to the save-time\n // guard so the COMPLETED transition is accepted (the guard rejects any\n // other route into COMPLETED).\n this.journalId = journal.id;\n this.status = PaymentStatus.COMPLETED;\n this.paidAt = new Date();\n settlementInProgress.add(this);\n await this.save();\n\n return journal;\n }\n\n /**\n * Get the linked journal entry (if recorded)\n */\n async getJournal(): Promise<any | null> {\n if (!this.journalId) return null;\n\n try {\n const { JournalCollection } = await import('@happyvertical/smrt-ledgers');\n const collection = await (JournalCollection as any).create(this.options);\n return await collection.get({ id: this.journalId });\n } catch {\n // smrt-ledgers not available\n return null;\n }\n }\n\n /**\n * Mark payment as failed\n */\n markFailed(reason?: string): void {\n this.status = PaymentStatus.FAILED;\n if (reason) {\n this.notes = `${this.notes ? `${this.notes}\\n` : ''}Failed: ${reason}`;\n }\n }\n\n /**\n * Cancel the payment\n */\n cancel(): void {\n if (this.status === PaymentStatus.COMPLETED) {\n throw new Error('Cannot cancel a completed payment. Use refund instead.');\n }\n this.status = PaymentStatus.CANCELLED;\n }\n}\n\nexport default Payment;\n","/**\n * PaymentCollection - Collection manager for Payment objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Payment } from '../models/Payment.js';\nimport { type PaymentMethod, PaymentStatus } from '../types/index.js';\n\nexport class PaymentCollection extends SmrtCollection<Payment> {\n static readonly _itemClass = Payment;\n\n /**\n * Find payments by contract\n *\n * @param contractId - Contract ID\n * @returns Array of payments\n */\n async findByContract(contractId: string): Promise<Payment[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find payments by customer\n *\n * @param customerId - Customer ID\n * @returns Array of payments\n */\n async findByCustomer(customerId: string): Promise<Payment[]> {\n return await this.list({\n where: { customerId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find payments by status\n *\n * @param status - Payment status\n * @returns Array of payments\n */\n async findByStatus(status: PaymentStatus): Promise<Payment[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find payments by method\n *\n * @param method - Payment method\n * @returns Array of payments\n */\n async findByMethod(method: PaymentMethod): Promise<Payment[]> {\n return await this.list({\n where: { method },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all pending payments\n *\n * @returns Array of pending payments\n */\n async findPending(): Promise<Payment[]> {\n return await this.findByStatus(PaymentStatus.PENDING);\n }\n\n /**\n * Find all completed payments\n *\n * @returns Array of completed payments\n */\n async findCompleted(): Promise<Payment[]> {\n return await this.findByStatus(PaymentStatus.COMPLETED);\n }\n\n /**\n * Find payment by transaction ID\n *\n * @param transactionId - External transaction ID\n * @returns Payment or null\n */\n async findByTransactionId(transactionId: string): Promise<Payment | null> {\n const results = await this.list({\n where: { transactionId },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Find payments in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of payments\n */\n async findByDateRange(startDate: Date, endDate: Date): Promise<Payment[]> {\n return await this.list({\n where: {\n 'paidAt >=': startDate.toISOString(),\n 'paidAt <=': endDate.toISOString(),\n },\n orderBy: 'paidAt DESC',\n });\n }\n\n /**\n * Calculate total payments for a contract\n *\n * @param contractId - Contract ID\n * @returns Total amount paid\n */\n async getTotalForContract(contractId: string): Promise<number> {\n const payments = await this.list({\n where: {\n contractId,\n status: PaymentStatus.COMPLETED,\n },\n });\n\n return payments.reduce((sum, payment) => sum + payment.amount, 0);\n }\n\n /**\n * Find payments linked to a ledger journal\n *\n * @param journalId - Journal ID from smrt-ledgers\n * @returns Payment or null\n */\n async findByJournal(journalId: string): Promise<Payment | null> {\n const results = await this.list({\n where: { journalId },\n limit: 1,\n });\n return results[0] || null;\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all payments belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of payments for the tenant\n */\n async findByTenant(tenantId: string): Promise<Payment[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payments (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 payments\n */\n async findGlobal(): Promise<Payment[]> {\n return queryGlobal<Payment>(this);\n }\n\n /**\n * Find payments for a tenant including global payments.\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 payments\n */\n async findWithGlobals(tenantId: string): Promise<Payment[]> {\n return queryWithGlobals<Payment>(this, tenantId, 'Payment.findWithGlobals');\n }\n}\n","/**\n * PaymentIntent — short-lived pre-payment commitment with multi-option\n * semantics.\n *\n * A PaymentIntent is a one-shot quote that locks a USD price for a fixed\n * window and lists one or more `PaymentOption`s describing different\n * payment rails (USDC-on-Base, BTC, Stripe, etc.) that can each satisfy\n * the intent. The first option that receives an incoming payment wins —\n * the intent transitions to {@link PaymentIntentStatus.PAID} and the\n * other options are implicitly retired. Later inbound funds to a\n * retired option must be flagged for refund by the consumer's\n * payment-routing layer.\n *\n * Distinct from `Estimate` (a long-form contract proposal STI subtype\n * of `Contract`): a `PaymentIntent` is short-lived (minutes, not days),\n * carries no line items, and exists to coordinate the moment-of-payment.\n *\n * Industry-neutral: any application that offers more than one route to\n * pay for the same thing — marketplaces, billing systems with multi-\n * provider checkout, agent-driven (x402) flows — benefits.\n *\n * @packageDocumentation\n */\n\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n PaymentIntentStatus,\n type PaymentOption,\n PaymentStatus,\n} from '../types/index.js';\n\n/**\n * Sub-cent rounding tolerance for native-amount reconciliation between the\n * winning PaymentOption and the referenced Payment row.\n */\nconst PAYMENT_INTENT_EPSILON = 0.01;\n\n/**\n * Legal status transitions for a PaymentIntent, keyed by the prior persisted\n * status. Mirrors the state machine documented on {@link PaymentIntentStatus}\n * and enforced by the dedicated helpers, so a raw `status` mass-assignment on\n * the generated update route can't skip steps (e.g. `awaiting_payment →\n * issued`) or revive a terminal state.\n *\n * `awaiting_payment → paid → (issued | retired)`, with `expired` / `cancelled`\n * as alternate terminal exits from `awaiting_payment`. `issued` may still be\n * `retired` (a reversal after rights were issued).\n */\nconst PAYMENT_INTENT_STATUS_TRANSITIONS: Record<\n PaymentIntentStatus,\n PaymentIntentStatus[]\n> = {\n [PaymentIntentStatus.AWAITING_PAYMENT]: [\n PaymentIntentStatus.PAID,\n PaymentIntentStatus.EXPIRED,\n PaymentIntentStatus.CANCELLED,\n ],\n [PaymentIntentStatus.PAID]: [\n PaymentIntentStatus.ISSUED,\n PaymentIntentStatus.RETIRED,\n ],\n [PaymentIntentStatus.ISSUED]: [PaymentIntentStatus.RETIRED],\n // Terminal states.\n [PaymentIntentStatus.RETIRED]: [],\n [PaymentIntentStatus.EXPIRED]: [],\n [PaymentIntentStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each PaymentIntent was loaded with, so\n * the save-time guard can detect a status being forced to PAID via raw\n * mass-assignment (bypassing the verified transition path). Same WeakMap\n * pattern as the rest of the package.\n */\nconst loadedIntentStatus = new WeakMap<PaymentIntent, PaymentIntentStatus>();\n\n/**\n * Default price-lock window — 15 minutes is a reasonable middle ground:\n * long enough for a buyer to swap wallets / approve a Stripe payment,\n * short enough that volatile-currency drift stays bounded.\n */\nconst DEFAULT_PRICE_LOCK_WINDOW_MS = 15 * 60 * 1000;\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Natural-key idempotency: a consumer that retries `create` with the\n // same (offeringRef, licenseeEmail, idempotencyKey) within the\n // retention window will hit the existing row via upsert rather than\n // creating a duplicate. Tenant id is part of the key so the same\n // idempotency key never collides across tenants.\n conflictColumns: [\n 'tenant_id',\n 'offering_ref',\n 'licensee_email',\n 'idempotency_key',\n ],\n // ROOT FIX (S5 audit #1390 round 4): the generated create/update surface may\n // only set the quote-definition fields. The PAID-state backing fields\n // (`status`, `paymentId`, `paidOptionBackendId`) are EXCLUDED — they are set\n // exclusively by the verified `verifyAndMarkPaid()` / `markIssued()` path, so\n // a caller can never forge a PAID intent (or repoint one) through a generated\n // route. `paymentOptions` IS writable (a quote may be re-priced while\n // AWAITING_PAYMENT) but is frozen by `save()` once the intent is PAID/ISSUED\n // (codex HIGH#2). Timestamps + priceLockExpiresAt are derived and excluded.\n api: {\n include: ['list', 'get', 'create', 'update'],\n writable: [\n 'skuId',\n 'offeringRef',\n 'licenseeEmail',\n 'customerId',\n 'paymentOptions',\n 'usdPriceLocked',\n 'priceLockWindowMs',\n 'idempotencyKey',\n 'notes',\n ],\n },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class PaymentIntent extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation. Nullable so the same model\n * can serve both per-tenant SaaS deployments and single-tenant /\n * global setups.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Plain string reference to the upstream `Sku` (from\n * `@happyvertical/smrt-products`) being purchased. Cross-package\n * reference — plain string, not `@foreignKey()`, to avoid the\n * circular dependency the framework warns against.\n *\n * Required for the marketplace flow; other consumers can leave it\n * empty if they don't model a catalog.\n */\n skuId: string = '';\n\n /**\n * Caller-supplied scope for the idempotency-key natural key. The\n * marketplace sets this to a `Sku` id, but the field is intentionally\n * abstract so other consumers (subscription tiers, donation tiers,\n * tipping flows) can scope idempotency by whatever granularity makes\n * sense.\n *\n * Empty string is permitted but disables the natural-key dedup —\n * every retry then creates a fresh row.\n */\n offeringRef: string = '';\n\n /**\n * The buyer's email address. The licensee on any downstream\n * `LicenseSale` (or similar rights-issuance row) is identified by\n * this email — high-tier purchases also create a `Customer` record\n * and set {@link customerId}, but most purchases get away with email\n * alone.\n */\n licenseeEmail: string = '';\n\n /**\n * Optional link to a `Customer` row for purchases that warrant a\n * full customer record (high-tier licensing, recurring buyers,\n * etc.). Cross-model reference is kept as a plain string to match\n * the rest of the package's cross-package convention.\n */\n customerId: string = '';\n\n /**\n * The payment rails the buyer can choose between to satisfy this\n * intent. Stored as a JSON column. See {@link PaymentOption} for\n * the per-option shape. Empty array means the intent is invalid /\n * mis-built; callers should reject save in that case.\n */\n paymentOptions: PaymentOption[] = [];\n\n /**\n * USD price locked at quote time. Stored at decimal precision.\n * Independent of any specific option's native-currency amount;\n * `usdPriceLocked` is the canonical \"what this costs in USD\"\n * number used downstream for reporting, tax, and drift accounting.\n */\n usdPriceLocked: number = 0.0;\n\n /**\n * Length of the price-lock window in milliseconds. Defaults to 15\n * minutes; consumers can override per-intent. Stored so a later\n * audit can see the original window even after {@link priceLockExpiresAt}\n * has passed.\n */\n priceLockWindowMs: number = DEFAULT_PRICE_LOCK_WINDOW_MS;\n\n /**\n * Wall-clock time at which the price lock expires. Set by the\n * constructor (or by `expire()` if you want to short-circuit a\n * specific intent). Once `Date.now()` passes this value and the\n * status is still `AWAITING_PAYMENT`, callers should treat the\n * intent as expired and refuse to record a payment against it.\n */\n priceLockExpiresAt: Date | null = null;\n\n /**\n * Current status — see {@link PaymentIntentStatus} for the state\n * machine. Mutate via the dedicated `markPaid` / `markIssued` /\n * `expire` / `cancel` / `retire` helpers rather than assigning\n * directly, so the helpers' invariant checks run.\n */\n status: PaymentIntentStatus = PaymentIntentStatus.AWAITING_PAYMENT;\n\n /**\n * Caller-supplied idempotency key. Combined with `tenantId`,\n * `offeringRef`, and `licenseeEmail`, this forms the natural key\n * registered in `conflictColumns` — a retried `create` with the\n * same tuple upserts the existing row instead of creating a\n * duplicate.\n *\n * Empty string disables natural-key dedup (every retry creates a\n * fresh row).\n */\n idempotencyKey: string = '';\n\n /**\n * `backendId` of the option that satisfied the intent. Set by\n * {@link markPaid}; remains empty for non-paid intents. Used by\n * {@link isOptionRetired} to flag inbound funds on the other\n * options as \"needs refund\".\n */\n paidOptionBackendId: string = '';\n\n /**\n * Plain string reference to the `Payment` row that satisfied the\n * intent. Cross-model reference matches the rest of the\n * package's convention.\n */\n paymentId: string = '';\n\n /**\n * When the intent transitioned to `PAID`.\n */\n paidAt: Date | null = null;\n\n /**\n * When the intent transitioned to `ISSUED`.\n */\n issuedAt: Date | null = null;\n\n /**\n * When the intent transitioned to `EXPIRED`.\n */\n expiredAt: Date | null = null;\n\n /**\n * When the intent transitioned to `CANCELLED`.\n */\n cancelledAt: Date | null = null;\n\n /**\n * When the intent transitioned to `RETIRED`.\n */\n retiredAt: Date | null = null;\n\n /**\n * Optional human-readable notes (e.g., support tickets, refund\n * memos, cancellation reasons). Append-only by convention.\n */\n notes: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.skuId !== undefined) this.skuId = options.skuId;\n if (options.offeringRef !== undefined)\n this.offeringRef = options.offeringRef;\n if (options.licenseeEmail !== undefined)\n this.licenseeEmail = options.licenseeEmail;\n if (options.customerId !== undefined) this.customerId = options.customerId;\n if (options.paymentOptions !== undefined)\n this.paymentOptions = PaymentIntent.normalizePaymentOptions(\n options.paymentOptions,\n );\n if (options.usdPriceLocked !== undefined)\n this.usdPriceLocked = options.usdPriceLocked;\n if (options.priceLockWindowMs !== undefined)\n this.priceLockWindowMs = options.priceLockWindowMs;\n if (options.priceLockExpiresAt !== undefined) {\n this.priceLockExpiresAt = PaymentIntent.coerceDate(\n options.priceLockExpiresAt,\n );\n }\n if (options.status !== undefined) this.status = options.status;\n if (options.idempotencyKey !== undefined)\n this.idempotencyKey = options.idempotencyKey;\n if (options.paidOptionBackendId !== undefined)\n this.paidOptionBackendId = options.paidOptionBackendId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.paidAt !== undefined)\n this.paidAt = PaymentIntent.coerceDate(options.paidAt);\n if (options.issuedAt !== undefined)\n this.issuedAt = PaymentIntent.coerceDate(options.issuedAt);\n if (options.expiredAt !== undefined)\n this.expiredAt = PaymentIntent.coerceDate(options.expiredAt);\n if (options.cancelledAt !== undefined)\n this.cancelledAt = PaymentIntent.coerceDate(options.cancelledAt);\n if (options.retiredAt !== undefined)\n this.retiredAt = PaymentIntent.coerceDate(options.retiredAt);\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Re-normalize `paymentOptions` after the framework's\n * `initializePropertiesFromOptions` pass has overwritten the\n * constructor-set values with the raw cloned input, and stamp a\n * default `priceLockExpiresAt` so newly-created intents have a\n * concrete expiry without callers needing to compute it.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.paymentOptions = PaymentIntent.normalizePaymentOptions(\n this.paymentOptions as unknown,\n );\n this.priceLockExpiresAt = PaymentIntent.coerceDate(this.priceLockExpiresAt);\n this.paidAt = PaymentIntent.coerceDate(this.paidAt);\n this.issuedAt = PaymentIntent.coerceDate(this.issuedAt);\n this.expiredAt = PaymentIntent.coerceDate(this.expiredAt);\n this.cancelledAt = PaymentIntent.coerceDate(this.cancelledAt);\n this.retiredAt = PaymentIntent.coerceDate(this.retiredAt);\n if (!this.priceLockExpiresAt) {\n // Anchor the price-lock window to \"now\" if no explicit expiry\n // was supplied. Using the configured window so consumers that\n // override `priceLockWindowMs` get the right anchor.\n this.priceLockExpiresAt = new Date(\n Date.now() + (this.priceLockWindowMs || DEFAULT_PRICE_LOCK_WINDOW_MS),\n );\n }\n if (await this.isSaved()) {\n loadedIntentStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Save-time state-machine guard (S5 audit #1390 round 2).\n *\n * `status`, `paymentId`, and `paidOptionBackendId` are all mass-assignable on\n * the generated update route, and the sync `markPaid` helper trusts its\n * arguments. Two distinct attacks follow:\n *\n * 1. **Forge a PAID intent** — set `status: 'paid'` (or call bare `markPaid`)\n * against a bogus / pending / non-matching Payment. This falsifies\n * downstream \"this was paid for\" rights issuance.\n * 2. **Repoint an already-PAID intent** — leave `status: 'paid'` but swap\n * `paymentId` / `paidOptionBackendId` to a bogus Payment after the fact.\n * Round 1 only verified the *transition into* PAID, so an already-PAID\n * row could be silently repointed with no re-verify. Likewise an intent\n * forced straight to `ISSUED` (the terminal happy-path that gates rights\n * issuance) was never required to have been backed by a real Payment.\n *\n * This guard therefore:\n * - validates the status transition is legal (no `awaiting_payment → issued`\n * skips, no reviving terminal states);\n * - re-verifies the backing Payment on **every** save where the persisted\n * status is PAID or ISSUED — not just the transition edge — so a repoint\n * of the paid backing fields is caught.\n *\n * Re-verifying an unchanged PAID/ISSUED row is cheap (one Payment load) and\n * closes the repoint hole; freezing the backing fields would instead block\n * legitimate corrections, so we re-verify.\n */\n override async save(): Promise<this> {\n const priorRow = await this.loadPersistedRow();\n const prior = priorRow\n ? (priorRow.status as PaymentIntentStatus)\n : loadedIntentStatus.get(this);\n this.assertStatusTransition(prior);\n\n // Freeze the backing fields once the intent is already PAID/ISSUED in the\n // database (codex HIGH#2). Round 3 re-verified the backing Payment but did\n // so against the *mutable* in-memory `paymentOptions`, so a caller could\n // swap `paymentId` to a different completed Payment AND edit the matching\n // option's `nativeAmount` so the reconciliation still passed — silently\n // repointing a settled intent at unrelated funds. A PAID/ISSUED intent's\n // economic identity (paymentId + paidOptionBackendId + usdPriceLocked +\n // paymentOptions) is settled and must not change; corrections go through\n // `retire()` + a fresh intent.\n if (\n priorRow &&\n (priorRow.status === PaymentIntentStatus.PAID ||\n priorRow.status === PaymentIntentStatus.ISSUED)\n ) {\n this.assertBackingFieldsUnchanged(priorRow);\n }\n\n const persistingPaidOrIssued =\n this.status === PaymentIntentStatus.PAID ||\n this.status === PaymentIntentStatus.ISSUED;\n\n if (persistingPaidOrIssued) {\n await this.assertBackedByCompletedPayment();\n }\n\n const result = (await super.save()) as this;\n loadedIntentStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Load the authoritative persisted row for this intent (S5 audit #1390 round\n * 4). Returns `undefined` when the intent has no `id` or no row exists yet\n * (truly new). Reading the DB directly — rather than trusting the\n * {@link loadedIntentStatus} WeakMap, which is only populated when\n * {@link initialize} hydrated the row — defeats the poisonable-prior-state\n * vector where `create({ id: <existing>, _skipLoad: true })` produces an\n * un-hydrated instance whose WeakMap entry is missing. A create-onto-existing\n * is thus correctly treated as an update.\n */\n private async loadPersistedRow(): Promise<Record<string, any> | undefined> {\n if (!this.id) return undefined;\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n return row ?? undefined;\n } catch {\n // DB not ready / table absent — treat as new (in-memory fallback in save).\n return undefined;\n }\n }\n\n /**\n * Reject any change to the settled backing fields of an already-PAID/ISSUED\n * intent (codex HIGH#2). `paymentOptions` is compared structurally because\n * the winning option's `nativeAmount` is exactly what the reconciliation in\n * {@link assertBackedByCompletedPayment} binds against — letting it drift\n * would let a repointed `paymentId` match a doctored option.\n */\n private assertBackingFieldsUnchanged(priorRow: Record<string, any>): void {\n const frozen: Array<[string, unknown, unknown]> = [\n ['paymentId', priorRow.payment_id ?? '', this.paymentId],\n [\n 'paidOptionBackendId',\n priorRow.paid_option_backend_id ?? '',\n this.paidOptionBackendId,\n ],\n [\n 'usdPriceLocked',\n Number(priorRow.usd_price_locked ?? 0),\n Number(this.usdPriceLocked ?? 0),\n ],\n ];\n for (const [field, prior, next] of frozen) {\n if (prior !== next) {\n throw new Error(\n `PaymentIntent ${this.id}: '${field}' is frozen once the intent is ` +\n `PAID/ISSUED (was '${prior}', got '${next}'). A settled intent's ` +\n 'backing fields cannot be repointed — retire() it and issue a new intent.',\n );\n }\n }\n\n const priorOptions = PaymentIntent.normalizePaymentOptions(\n priorRow.payment_options as unknown,\n );\n const nextOptions = PaymentIntent.normalizePaymentOptions(\n this.paymentOptions as unknown,\n );\n if (JSON.stringify(priorOptions) !== JSON.stringify(nextOptions)) {\n throw new Error(\n `PaymentIntent ${this.id}: 'paymentOptions' is frozen once the intent ` +\n 'is PAID/ISSUED — the winning option backs the verified Payment ' +\n 'reconciliation and cannot change. retire() it and issue a new intent.',\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 re-saves (status unchanged) and brand-new rows are allowed (the\n * backing-Payment verification still runs separately for PAID/ISSUED).\n */\n private assertStatusTransition(prior: PaymentIntentStatus | undefined): void {\n if (prior === undefined) return; // new row\n if (prior === this.status) return; // no-op re-save\n const allowed = PAYMENT_INTENT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `PaymentIntent ${this.id}: illegal status transition '${prior}' → ` +\n `'${this.status}'. Use the guarded transition helpers ` +\n '(verifyAndMarkPaid / markIssued / expire / cancel / retire).',\n );\n }\n }\n\n /**\n * Verify the intent's `paidOptionBackendId` / `paymentId` reference a real,\n * COMPLETED, amount-matching Payment. Throws otherwise. Shared by\n * {@link verifyAndMarkPaid} (pre-transition) and the save-time guard\n * (catch-all for raw mass-assignment).\n */\n private async assertBackedByCompletedPayment(): Promise<void> {\n if (!this.paidOptionBackendId) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot persist PAID status without a paidOptionBackendId`,\n );\n }\n if (!this.paymentId) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot persist PAID status without a paymentId`,\n );\n }\n const option = this.getOption(this.paidOptionBackendId);\n if (!option) {\n throw new Error(\n `PaymentIntent ${this.id}: paidOptionBackendId '${this.paidOptionBackendId}' is not one of the listed options`,\n );\n }\n\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await (PaymentCollection as any).create(this.options);\n const payment = await payments.get({ id: this.paymentId });\n if (!payment) {\n throw new Error(\n `PaymentIntent ${this.id}: referenced Payment '${this.paymentId}' does not exist`,\n );\n }\n if (payment.status !== PaymentStatus.COMPLETED) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${this.paymentId}' is not COMPLETED ` +\n `(status='${payment.status}'); cannot persist PAID intent against it`,\n );\n }\n this.reconcilePaymentWithOption(payment, option, this.paymentId);\n }\n\n /**\n * Reconcile a COMPLETED Payment against the winning {@link PaymentOption}\n * (S5 audit #1390). Beyond the amount check, the Payment must have arrived on\n * the SAME rail and currency the option quoted — otherwise an unrelated\n * completed payment that merely shares a numeric amount (e.g. a USD 199\n * payment) could satisfy a different option (e.g. a `base-usdc` 199 option),\n * marking the intent paid on the wrong rail with no matching funds. Compares\n * the Payment's `backendId` (rail) and native currency (falling back to its\n * settlement currency) against the option's `backendId` / `currency`, then\n * the native amount.\n *\n * Shared by {@link verifyAndMarkPaid} (pre-transition) and\n * {@link assertBackedByCompletedPayment} (save-time catch-all) so both gates\n * enforce the identical invariant.\n */\n private reconcilePaymentWithOption(\n payment: {\n backendId?: string;\n nativeCurrency?: string;\n currency?: string;\n nativeAmount?: number;\n amount?: number;\n },\n option: PaymentOption,\n paymentIdLabel: string,\n ): void {\n // Only enforce the rail check when the Payment actually recorded a backend\n // (PaymentBackend-routed flows). Manually-recorded payments may carry no\n // backendId; the amount + currency checks still apply.\n if (payment.backendId && payment.backendId !== option.backendId) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${paymentIdLabel}' arrived on rail ` +\n `'${payment.backendId}' but option '${option.backendId}' expects rail ` +\n `'${option.backendId}' — cannot mark paid on a mismatched rail`,\n );\n }\n // The option's `currency` is the native-rail-qualified code (e.g.\n // `USDC-base`), which lines up with the Payment's `nativeCurrency`. Fall\n // back to the settlement `currency` when no native currency was recorded.\n const paymentCurrency = payment.nativeCurrency || payment.currency || '';\n if (paymentCurrency && paymentCurrency !== option.currency) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${paymentIdLabel}' currency ` +\n `'${paymentCurrency}' does not match option '${option.backendId}' ` +\n `currency '${option.currency}'`,\n );\n }\n const paymentNative =\n typeof payment.nativeAmount === 'number' && payment.nativeAmount > 0\n ? payment.nativeAmount\n : payment.amount;\n if (\n typeof paymentNative !== 'number' ||\n Math.abs(paymentNative - option.nativeAmount) > PAYMENT_INTENT_EPSILON\n ) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${paymentIdLabel}' amount ${paymentNative} ` +\n `does not match option '${option.backendId}' nativeAmount ${option.nativeAmount}`,\n );\n }\n }\n\n // -------- Status helpers --------\n\n isAwaitingPayment(): boolean {\n return this.status === PaymentIntentStatus.AWAITING_PAYMENT;\n }\n\n isPaid(): boolean {\n return this.status === PaymentIntentStatus.PAID;\n }\n\n isIssued(): boolean {\n return this.status === PaymentIntentStatus.ISSUED;\n }\n\n isCancelled(): boolean {\n return this.status === PaymentIntentStatus.CANCELLED;\n }\n\n isRetired(): boolean {\n return this.status === PaymentIntentStatus.RETIRED;\n }\n\n /**\n * Terminal-state predicate: any status other than `AWAITING_PAYMENT`\n * is terminal (the intent will not accept further state changes\n * except the explicit `markIssued` after `PAID`).\n */\n isTerminal(): boolean {\n return this.status !== PaymentIntentStatus.AWAITING_PAYMENT;\n }\n\n /**\n * `Date.now()`-based predicate: has the price lock window passed?\n * Independent of `status` so callers can detect a stale-but-not-\n * yet-marked-EXPIRED intent (and call `expire()` to advance it).\n */\n isExpired(): boolean {\n if (this.status === PaymentIntentStatus.EXPIRED) return true;\n if (!this.priceLockExpiresAt) return false;\n return Date.now() > this.priceLockExpiresAt.getTime();\n }\n\n // -------- State transitions --------\n\n /**\n * Transition the intent to `PAID`, naming which option satisfied it\n * and which `Payment` row carries the funds. Implicitly retires the\n * other options — callers can check {@link isOptionRetired} to flag\n * later inbound funds for refund.\n *\n * Throws when called on a non-`AWAITING_PAYMENT` intent so a stale\n * caller can't accidentally overwrite a winning option with a\n * losing one.\n */\n markPaid(args: { backendId: string; paymentId: string }): void {\n if (this.status !== PaymentIntentStatus.AWAITING_PAYMENT) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot transition to PAID from status '${this.status}'`,\n );\n }\n // Refuse stale quotes. A backend can report a payment after the price-lock\n // window has passed but before a cleanup job has run `expire()` — the\n // intent is still AWAITING_PAYMENT, yet honoring it would accept funds at\n // an expired USD price. `isExpired()` is status-independent (it checks\n // `priceLockExpiresAt`), so guard on it here; callers should `expire()`\n // and re-quote rather than record against a lapsed lock.\n if (this.isExpired()) {\n throw new Error(\n `PaymentIntent ${this.id}: price lock expired at ` +\n `${this.priceLockExpiresAt?.toISOString() ?? 'unknown'}; cannot record a ` +\n `payment against a stale quote (call expire() and re-quote)`,\n );\n }\n if (!args.backendId) {\n throw new Error(\n `PaymentIntent ${this.id}: markPaid requires a backendId`,\n );\n }\n const option = this.paymentOptions.find(\n (o) => o.backendId === args.backendId,\n );\n if (!option) {\n throw new Error(\n `PaymentIntent ${this.id}: backendId '${args.backendId}' is not one of the listed options`,\n );\n }\n this.status = PaymentIntentStatus.PAID;\n this.paidOptionBackendId = args.backendId;\n this.paymentId = args.paymentId ?? '';\n this.paidAt = new Date();\n }\n\n /**\n * Verify-then-transition variant of {@link markPaid} (S5 audit #1390).\n *\n * `markPaid` trusts the caller-supplied `paymentId` / `backendId` without\n * checking the referenced `Payment` actually exists, is `COMPLETED`, or\n * carries the amount the winning option quoted. That lets a caller mark an\n * intent PAID against a non-existent or still-pending payment. This method\n * loads the `Payment` row and enforces:\n *\n * - the Payment exists,\n * - it is `COMPLETED` (not pending / failed / cancelled / refunded),\n * - it arrived on the same rail (`backendId`) and currency the option\n * quoted (so an unrelated completed payment that merely shares a numeric\n * amount can't satisfy a different option),\n * - and its amount reconciles with the winning option's `nativeAmount`\n * (within sub-cent tolerance), falling back to the option vs. the\n * Payment's settlement `amount` when no native rail is recorded.\n *\n * Only after those pass does it delegate to the sync `markPaid` for the\n * status-machine invariants.\n *\n * @param args.backendId backendId of the option that was satisfied\n * @param args.paymentId id of the Payment row carrying the funds (required)\n */\n async verifyAndMarkPaid(args: {\n backendId: string;\n paymentId: string;\n }): Promise<void> {\n if (!args.paymentId) {\n throw new Error(\n `PaymentIntent ${this.id}: verifyAndMarkPaid requires a paymentId`,\n );\n }\n const option = this.paymentOptions.find(\n (o) => o.backendId === args.backendId,\n );\n if (!option) {\n throw new Error(\n `PaymentIntent ${this.id}: backendId '${args.backendId}' is not one of the listed options`,\n );\n }\n\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await (PaymentCollection as any).create(this.options);\n const payment = await payments.get({ id: args.paymentId });\n if (!payment) {\n throw new Error(\n `PaymentIntent ${this.id}: referenced Payment '${args.paymentId}' does not exist`,\n );\n }\n if (payment.status !== PaymentStatus.COMPLETED) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${args.paymentId}' is not COMPLETED ` +\n `(status='${payment.status}'); cannot mark intent PAID against it`,\n );\n }\n\n // Reconcile the payment against the winning option — same rail, currency,\n // and amount (shared with the save-time guard so both gates agree).\n this.reconcilePaymentWithOption(payment, option, args.paymentId);\n\n this.markPaid({ backendId: args.backendId, paymentId: args.paymentId });\n }\n\n /**\n * Transition a `PAID` intent to `ISSUED` once the downstream rights\n * / contract / fulfillment have been created. Idempotent — calling\n * twice is a no-op so callers don't have to guard against retries.\n */\n markIssued(): void {\n if (this.status === PaymentIntentStatus.ISSUED) return;\n if (this.status !== PaymentIntentStatus.PAID) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot transition to ISSUED from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.ISSUED;\n this.issuedAt = new Date();\n }\n\n /**\n * Move an `AWAITING_PAYMENT` intent to `EXPIRED`. Safe to call on\n * an already-expired intent (no-op). Throws on terminal non-\n * expired states so a confused caller can't undo a paid / issued\n * intent.\n */\n expire(): void {\n if (this.status === PaymentIntentStatus.EXPIRED) return;\n if (this.status !== PaymentIntentStatus.AWAITING_PAYMENT) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot expire from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.EXPIRED;\n this.expiredAt = new Date();\n }\n\n /**\n * Cancel an open intent. Only valid from `AWAITING_PAYMENT` —\n * a paid / issued intent is no longer the buyer's to cancel; that\n * path is a refund, modelled separately on `Payment`.\n */\n cancel(reason?: string): void {\n if (this.status !== PaymentIntentStatus.AWAITING_PAYMENT) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot cancel from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.CANCELLED;\n this.cancelledAt = new Date();\n if (reason) {\n this.notes = this.notes\n ? `${this.notes}\\nCancelled: ${reason}`\n : `Cancelled: ${reason}`;\n }\n }\n\n /**\n * Mark a paid intent as retired — used when a previously-recorded\n * payment was reversed (chargeback, refund) and the intent should\n * not be considered \"satisfied\" anymore. Distinct from `cancel()`\n * which only applies to never-paid intents.\n */\n retire(reason?: string): void {\n if (\n this.status !== PaymentIntentStatus.PAID &&\n this.status !== PaymentIntentStatus.ISSUED\n ) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot retire from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.RETIRED;\n this.retiredAt = new Date();\n if (reason) {\n this.notes = this.notes\n ? `${this.notes}\\nRetired: ${reason}`\n : `Retired: ${reason}`;\n }\n }\n\n // -------- Option helpers --------\n\n /**\n * Return the option whose `backendId` matches, or `undefined` if\n * no such option is on this intent. Useful for routing inbound\n * payments to the right option's `nativeAmount` / `payTo` for\n * verification.\n */\n getOption(backendId: string): PaymentOption | undefined {\n return this.paymentOptions.find((o) => o.backendId === backendId);\n }\n\n /**\n * `true` once the intent is paid and the given option was NOT the\n * winning one. Consumers use this to flag inbound funds to retired\n * options for refund. Returns `false` for the winning option, for\n * unpaid intents, and for an unknown `backendId`.\n */\n isOptionRetired(backendId: string): boolean {\n if (!this.isPaid() && !this.isIssued() && !this.isRetired()) return false;\n if (!this.paidOptionBackendId) return false;\n if (!this.getOption(backendId)) return false;\n return backendId !== this.paidOptionBackendId;\n }\n\n /**\n * The options that were NOT selected at payment time. Returns an\n * empty array for unpaid intents so callers can iterate without\n * special-casing.\n */\n getRetiredOptions(): PaymentOption[] {\n if (!this.paidOptionBackendId) return [];\n return this.paymentOptions.filter(\n (o) => o.backendId !== this.paidOptionBackendId,\n );\n }\n\n // -------- Normalization helpers --------\n\n /**\n * Defensive coercion for `paymentOptions` input. Accepts either an\n * already-parsed array or a JSON string (e.g. from a row whose\n * column was hand-edited or migrated from a different schema).\n * Drops entries that don't carry the required `backendId` /\n * `currency` / `payTo` / `nativeAmount` quartet so downstream\n * routing code can rely on the invariant.\n */\n private static normalizePaymentOptions(value: unknown): PaymentOption[] {\n if (value == null) return [];\n let parsed: unknown = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(parsed)) return [];\n const out: PaymentOption[] = [];\n for (const raw of parsed) {\n if (!raw || typeof raw !== 'object') continue;\n const o = raw as Record<string, unknown>;\n const backendId = typeof o.backendId === 'string' ? o.backendId : '';\n const currency = typeof o.currency === 'string' ? o.currency : '';\n const payTo = typeof o.payTo === 'string' ? o.payTo : '';\n const nativeAmount =\n typeof o.nativeAmount === 'number' ? o.nativeAmount : Number.NaN;\n if (!backendId || !currency || !payTo || !Number.isFinite(nativeAmount)) {\n continue;\n }\n const option: PaymentOption = {\n backendId,\n currency,\n payTo,\n nativeAmount,\n };\n if (typeof o.chain === 'string' && o.chain) option.chain = o.chain;\n if (typeof o.memo === 'string' && o.memo) option.memo = o.memo;\n if (typeof o.x402Capable === 'boolean') {\n option.x402Capable = o.x402Capable;\n }\n if (typeof o.expiresAt === 'string' && o.expiresAt) {\n option.expiresAt = o.expiresAt;\n }\n out.push(option);\n }\n return out;\n }\n\n /**\n * Coerce a Date-ish input (Date / number / ISO string / null /\n * undefined) into a `Date | null`. The framework hands us strings\n * when hydrating from SQLite, numbers when round-tripping through\n * JSON, and Date instances from the application.\n */\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default PaymentIntent;\n","/**\n * PaymentIntentCollection — collection manager for {@link PaymentIntent}.\n *\n * Adds helpers for the common query shapes (find by idempotency key,\n * find still-open intents, find by licensee email, find expired) and a\n * `getOrCreateByIdempotencyKey` helper that callers use to implement\n * safe at-least-once retries from the marketplace's PaymentIntent\n * issuance handler.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { PaymentIntent } from '../models/PaymentIntent.js';\nimport { PaymentIntentStatus, type PaymentOption } from '../types/index.js';\n\nexport interface PaymentIntentIdempotencyArgs {\n /** Caller-supplied scope (Sku id / tier id / etc.) */\n offeringRef: string;\n /** Buyer email — second factor of the natural key */\n licenseeEmail: string;\n /** Caller-supplied idempotency key */\n idempotencyKey: string;\n /** Optional tenant scope, when called outside an active tenant context */\n tenantId?: string | null;\n}\n\nexport interface PaymentIntentSeed\n extends Partial<Omit<PaymentIntentIdempotencyArgs, 'tenantId'>> {\n tenantId?: string | null;\n skuId?: string;\n customerId?: string;\n paymentOptions: PaymentOption[];\n usdPriceLocked: number;\n priceLockWindowMs?: number;\n}\n\nexport class PaymentIntentCollection extends SmrtCollection<PaymentIntent> {\n static readonly _itemClass = PaymentIntent;\n\n /**\n * Look up an existing intent by its natural key. Returns `null` when\n * any of the three components are empty — empty natural-key inputs\n * disable dedup by design, so a `null` return tells the caller to\n * fall through to a fresh `create`.\n */\n async findByIdempotencyKey(\n args: PaymentIntentIdempotencyArgs,\n ): Promise<PaymentIntent | null> {\n if (!args.offeringRef || !args.licenseeEmail || !args.idempotencyKey) {\n return null;\n }\n const where: Record<string, unknown> = {\n offeringRef: args.offeringRef,\n licenseeEmail: args.licenseeEmail,\n idempotencyKey: args.idempotencyKey,\n };\n if (args.tenantId !== undefined) {\n where.tenantId = args.tenantId;\n }\n const results = await this.list({ where, limit: 1 });\n return results[0] ?? null;\n }\n\n /**\n * Idempotency-aware create. If an intent already exists with the\n * same `(tenantId, offeringRef, licenseeEmail, idempotencyKey)`\n * tuple, returns the existing row. Otherwise creates a new one\n * with the supplied seed.\n *\n * Returns a `{ intent, created }` pair so callers can branch on\n * whether they're handling a fresh quote or a replay (e.g. to\n * skip a duplicate \"intent created\" webhook emission).\n */\n async getOrCreateByIdempotencyKey(\n seed: PaymentIntentSeed,\n ): Promise<{ intent: PaymentIntent; created: boolean }> {\n const existing = await this.findByIdempotencyKey({\n offeringRef: seed.offeringRef ?? '',\n licenseeEmail: seed.licenseeEmail ?? '',\n idempotencyKey: seed.idempotencyKey ?? '',\n tenantId: seed.tenantId,\n });\n if (existing) {\n return { intent: existing, created: false };\n }\n return { intent: await this.create(seed as any), created: true };\n }\n\n /**\n * Open intents — `AWAITING_PAYMENT` and not yet past the price-\n * lock window. The price-lock check is `Date.now()`-based, so a\n * stale-but-not-yet-marked-EXPIRED row drops out of this list even\n * before a cleanup job advances its status.\n */\n async findOpen(): Promise<PaymentIntent[]> {\n const open = await this.list({\n where: { status: PaymentIntentStatus.AWAITING_PAYMENT },\n orderBy: 'created_at DESC',\n });\n const now = Date.now();\n return open.filter((p) => {\n if (!p.priceLockExpiresAt) return true;\n return p.priceLockExpiresAt.getTime() > now;\n });\n }\n\n /**\n * `AWAITING_PAYMENT` intents whose price-lock window has already\n * passed. The expected use is a periodic cleanup job that loads\n * this list, calls `intent.expire(); await intent.save()` on each,\n * and emits whatever downstream signal the application needs.\n */\n async findStale(): Promise<PaymentIntent[]> {\n const open = await this.list({\n where: { status: PaymentIntentStatus.AWAITING_PAYMENT },\n });\n const now = Date.now();\n return open.filter((p) => {\n if (!p.priceLockExpiresAt) return false;\n return p.priceLockExpiresAt.getTime() <= now;\n });\n }\n\n async findByLicenseeEmail(email: string): Promise<PaymentIntent[]> {\n return this.list({\n where: { licenseeEmail: email },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByOfferingRef(offeringRef: string): Promise<PaymentIntent[]> {\n return this.list({\n where: { offeringRef },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByStatus(status: PaymentIntentStatus): Promise<PaymentIntent[]> {\n return this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n async findByTenant(tenantId: string): Promise<PaymentIntent[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payment intents (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 async findGlobal(): Promise<PaymentIntent[]> {\n return queryGlobal<PaymentIntent>(this);\n }\n\n /**\n * Find payment intents for a tenant including global intents.\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 async findWithGlobals(tenantId: string): Promise<PaymentIntent[]> {\n return queryWithGlobals<PaymentIntent>(\n this,\n tenantId,\n 'PaymentIntent.findWithGlobals',\n );\n }\n}\n","/**\n * Payout — operator-to-supplier outgoing remittance.\n *\n * A Payout sits on the opposite side of a {@link Payment}: it tracks\n * funds *leaving* the operator's wallet and arriving at a vendor's\n * payout address. The two are kept as distinct models (rather than\n * overloading `Payment` with a `direction` field) because the status\n * machine, chain semantics, and audit story differ.\n *\n * Industry-neutral: any operator that ingests one payment and remits\n * funds to a third party — marketplaces, payment processors with\n * Connect-style fan-out, royalty distributors, escrow services —\n * benefits.\n *\n * MVP scope: no currency conversion. The payout's amounts and currency\n * mirror the originating Payment's native currency, and the vendor\n * needs a matching `Vendor.payoutAddresses` entry to receive funds.\n * Filtering at quote time (`PaymentIntent.paymentOptions`) is the\n * consumer's job; the model just enforces the natural-key invariant\n * and the status machine.\n *\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { PayoutStatus } from '../types/index.js';\nimport { Vendor } from './Vendor.js';\n\n/**\n * Sub-cent rounding tolerance, matching the rest of the package's EPSILON\n * convention.\n */\nconst PAYOUT_EPSILON = 0.01;\n\n/**\n * Legal status transitions for a Payout, keyed by the prior persisted status.\n * Mirrors the state machine enforced by `markSent` / `markConfirmed` /\n * `markFailed` / `resetFromFailed`, so a raw `status` mass-assignment on the\n * generated update route can't skip steps (e.g. `pending → confirmed`) or\n * revive a terminal-ish state outside the dedicated reset path.\n *\n * `pending → sent → confirmed`, with `failed` reachable from `pending` / `sent`\n * and `failed → pending` allowed only via `resetFromFailed()`.\n */\nconst PAYOUT_STATUS_TRANSITIONS: Record<PayoutStatus, PayoutStatus[]> = {\n [PayoutStatus.PENDING]: [PayoutStatus.SENT, PayoutStatus.FAILED],\n [PayoutStatus.SENT]: [PayoutStatus.CONFIRMED, PayoutStatus.FAILED],\n [PayoutStatus.CONFIRMED]: [],\n // FAILED is resettable to PENDING via resetFromFailed().\n [PayoutStatus.FAILED]: [PayoutStatus.PENDING],\n};\n\n/**\n * Module-scoped record of the status each Payout instance was loaded with, so\n * the save-time transition guard can compare the prior persisted status\n * against the one being written. WeakMap keeps it out of the schema and GCs\n * with the instance — same pattern as the other commerce models.\n */\nconst loadedPayoutStatus = new WeakMap<Payout, PayoutStatus>();\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // ROOT FIX (S5 audit #1390 round 4): a Payout has NO safe generated write.\n // Its status drives an outgoing remittance and its amount triple\n // (grossAmount / operatorFee / supplierNet) is the integrity core — none may\n // be set by a caller. The only legitimate way to mint a payout is the\n // verified `PayoutCollection.createFromPayment()` domain path (which derives\n // the amounts from the source Payment and starts PENDING), and the only legal\n // status moves are the guarded `markSent` / `markConfirmed` / `markFailed` /\n // `resetFromFailed` helpers. So the generated surface is read-only — this\n // closes the \"forged CONFIRMED Payout via api.create\" vector (codex HIGH#4)\n // at the surface, not just in the save() guard.\n //\n // CLI parity (round 6, codex ROOT INSIGHT): the CLI is an independently\n // configured write surface. `cli: true` would still generate create/update/\n // delete commands that set status / the gross-fee-net triple, re-opening the\n // exact vector closed on api/mcp. The CLI is locked to the same read-only\n // surface — there is no safe generated payout write on ANY surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class Payout extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation. Nullable so global / single-\n * tenant deployments work too.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The source {@link Payment} that funded this payout. Plain string\n * reference (FK convention: `@foreignKey('ModelName')` would be\n * fine within the package, but `Payment` lives in this same\n * package — staying with a string keeps the dependency graph\n * clean and matches how `PaymentIntent` references `Payment`).\n */\n paymentId: string = '';\n\n /**\n * The destination {@link Vendor}. Foreign-key constrained because\n * Vendor lives in this same package and a hard reference catches\n * dangling payouts at insert time.\n */\n @foreignKey(Vendor)\n vendorId: string = '';\n\n /**\n * Total funds moved, in the originating payment's native currency.\n * Stored at decimal precision (matches `Payment.amount` /\n * `Payment.nativeAmount` convention).\n */\n grossAmount: number = 0.0;\n\n /**\n * Operator's take from `grossAmount`. Must equal\n * `grossAmount - supplierNet` at save time — the model enforces\n * the invariant via `validateAmounts()`.\n */\n operatorFee: number = 0.0;\n\n /**\n * Net funds the supplier receives. Must equal\n * `grossAmount - operatorFee` at save time.\n */\n supplierNet: number = 0.0;\n\n /**\n * Native currency the funds are denominated in — payout-rail-\n * qualified, matching `Payment.nativeCurrency` and\n * `Vendor.payoutAddresses` keys (`USDC-base`, `BTC`, `USD-stripe`,\n * etc.).\n */\n currency: string = '';\n\n /**\n * The `PaymentBackend` adapter id used to send the payout. For a\n * marketplace this typically matches the source `Payment.backendId`\n * (currency-match settlement, no conversion); other consumers can\n * use a different backend if they bridge currencies elsewhere.\n */\n backendId: string = '';\n\n /**\n * Outgoing chain transaction hash, gateway settlement id, or\n * (for not-yet-broadcast BTC payouts) a PSBT identifier. Set when\n * the payout transitions to `SENT`. Empty until then.\n */\n backendTxRef: string = '';\n\n /**\n * Current status — see {@link PayoutStatus} for the state machine.\n * Mutate via `markSent` / `markConfirmed` / `markFailed` /\n * `resetFromFailed` rather than direct assignment so the\n * transition invariants run.\n */\n status: PayoutStatus = PayoutStatus.PENDING;\n\n /**\n * When the payout transitioned to `SENT`.\n */\n sentAt: Date | null = null;\n\n /**\n * When the payout transitioned to `CONFIRMED`.\n */\n confirmedAt: Date | null = null;\n\n /**\n * When the payout transitioned to `FAILED`.\n */\n failedAt: Date | null = null;\n\n /**\n * Caller-supplied human-readable failure reason. Empty for non-\n * failed payouts. Cleared by `resetFromFailed()`.\n */\n failureReason: string = '';\n\n /**\n * Append-only operator notes — chargeback memos, manual-retry\n * justifications, etc.\n */\n notes: string = '';\n\n constructor(options: any = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.vendorId !== undefined) this.vendorId = options.vendorId;\n if (options.grossAmount !== undefined)\n this.grossAmount = options.grossAmount;\n if (options.operatorFee !== undefined)\n this.operatorFee = options.operatorFee;\n if (options.supplierNet !== undefined)\n this.supplierNet = options.supplierNet;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.backendId !== undefined) this.backendId = options.backendId;\n if (options.backendTxRef !== undefined)\n this.backendTxRef = options.backendTxRef;\n if (options.status !== undefined) this.status = options.status;\n if (options.sentAt !== undefined)\n this.sentAt = Payout.coerceDate(options.sentAt);\n if (options.confirmedAt !== undefined)\n this.confirmedAt = Payout.coerceDate(options.confirmedAt);\n if (options.failedAt !== undefined)\n this.failedAt = Payout.coerceDate(options.failedAt);\n if (options.failureReason !== undefined)\n this.failureReason = options.failureReason;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Re-coerce timestamp fields after the framework reapplies raw option\n * values during initialization.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.sentAt = Payout.coerceDate(this.sentAt);\n this.confirmedAt = Payout.coerceDate(this.confirmedAt);\n this.failedAt = Payout.coerceDate(this.failedAt);\n if (await this.isSaved()) {\n loadedPayoutStatus.set(this, this.status);\n }\n return this;\n }\n\n // -------- Status predicates --------\n\n isPending(): boolean {\n return this.status === PayoutStatus.PENDING;\n }\n\n isSent(): boolean {\n return this.status === PayoutStatus.SENT;\n }\n\n isConfirmed(): boolean {\n return this.status === PayoutStatus.CONFIRMED;\n }\n\n isFailed(): boolean {\n return this.status === PayoutStatus.FAILED;\n }\n\n /**\n * Transition `PENDING → SENT`. Requires `backendTxRef` — without a\n * way to identify the outgoing transaction the payout cannot be\n * tracked further. Throws on any other source status so a confused\n * caller can't accidentally roll back a confirmed payout.\n */\n markSent(backendTxRef: string): void {\n if (this.status !== PayoutStatus.PENDING) {\n throw new Error(\n `Payout ${this.id}: cannot transition to SENT from status '${this.status}'`,\n );\n }\n if (!backendTxRef) {\n throw new Error(`Payout ${this.id}: markSent requires a backendTxRef`);\n }\n this.backendTxRef = backendTxRef;\n this.status = PayoutStatus.SENT;\n this.sentAt = new Date();\n }\n\n /**\n * Transition `SENT → CONFIRMED`. Throws if called from any other\n * status — confirmations on a pending payout would mean a chain\n * confirmation arrived before the application even recorded the\n * outgoing tx, which is a code bug worth surfacing rather than\n * silently fixing.\n */\n markConfirmed(): void {\n if (this.status !== PayoutStatus.SENT) {\n throw new Error(\n `Payout ${this.id}: cannot transition to CONFIRMED from status '${this.status}'`,\n );\n }\n this.status = PayoutStatus.CONFIRMED;\n this.confirmedAt = new Date();\n }\n\n /**\n * Move the payout to `FAILED`. Valid from `PENDING` (couldn't even\n * broadcast) or `SENT` (broadcast but chain rejected). Confirmed\n * payouts cannot fail — that path is a refund, modelled separately.\n */\n markFailed(reason: string): void {\n if (\n this.status !== PayoutStatus.PENDING &&\n this.status !== PayoutStatus.SENT\n ) {\n throw new Error(\n `Payout ${this.id}: cannot transition to FAILED from status '${this.status}'`,\n );\n }\n this.status = PayoutStatus.FAILED;\n this.failedAt = new Date();\n this.failureReason = reason ?? '';\n }\n\n /**\n * Operator-driven reset: move a `FAILED` payout back to `PENDING`\n * after fixing whatever broke. Clears the failure metadata and the\n * old `backendTxRef` (the next attempt will have a new one).\n * Distinct from `markFailed` reflexivity — the issue spec says\n * \"failed is terminal but allows manual reset via explicit method\".\n */\n resetFromFailed(): void {\n if (this.status !== PayoutStatus.FAILED) {\n throw new Error(\n `Payout ${this.id}: cannot reset from status '${this.status}' — only FAILED is resettable`,\n );\n }\n this.status = PayoutStatus.PENDING;\n this.sentAt = null;\n this.confirmedAt = null;\n this.failedAt = null;\n this.failureReason = '';\n // Drop the old tx ref so the next markSent attempt gets a fresh one.\n this.backendTxRef = '';\n }\n\n /**\n * Throws if the gross/fee/net invariant doesn't hold. Tolerates a\n * sub-cent rounding fuzz (`EPSILON = 0.01`) to match the rest of\n * the smrt-ledgers package's tolerance.\n */\n validateAmounts(): void {\n const EPSILON = 0.01;\n // Non-negativity guard (S5 audit #1390). A negative operatorFee would let\n // an operator manufacture a supplierNet larger than the gross it took in\n // (paying out more than it received); negative gross/net are nonsensical.\n for (const [field, value] of [\n ['grossAmount', this.grossAmount],\n ['operatorFee', this.operatorFee],\n ['supplierNet', this.supplierNet],\n ] as const) {\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: ${field} must be a non-negative number (got ${value}).`,\n );\n }\n }\n const expectedNet = this.grossAmount - this.operatorFee;\n if (Math.abs(this.supplierNet - expectedNet) > EPSILON) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: amount invariant violated — ` +\n `gross=${this.grossAmount} fee=${this.operatorFee} net=${this.supplierNet} ` +\n `(expected net=${expectedNet})`,\n );\n }\n }\n\n /**\n * Save-time state-machine + settlement guard (S5 audit #1390 round 2).\n *\n * `status`, `backendTxRef`, and the amount triple are all mass-assignable on\n * the generated create/update routes. The amount invariant alone (round 1)\n * still let a caller raw-flip a persisted payout to `CONFIRMED` / `SENT`\n * (skipping the chain steps) or remit a `grossAmount` larger than the source\n * Payment actually brought in — money that never arrived.\n *\n * This guard adds:\n * - **Transitions on an existing row must be legal** per\n * {@link PAYOUT_STATUS_TRANSITIONS} (no `pending → confirmed` skip on a\n * raw update, no reviving a CONFIRMED payout). Brand-new rows may be\n * created in any status (collection/import/query fixtures), but advancing\n * a persisted row is constrained to the legal edges the helpers enforce.\n * - **SENT requires a backendTxRef** (matches `markSent`'s invariant) so a\n * sent payout is always traceable, regardless of how the status was set.\n * - **grossAmount is capped by the source Payment** when that Payment\n * resolves: a payout can never remit more than the funding Payment\n * settled. A `paymentId` that doesn't resolve (synthetic id, Payment-less\n * deployment) skips the cap — the source Payment is a plain-string,\n * cross-model reference by design and may legitimately be a manually\n * recorded (non-COMPLETED) row, so the cap is best-effort rather than a\n * hard existence requirement.\n *\n * The amount-invariant check (`validateAmounts`) still runs first as the\n * arithmetic floor.\n */\n override async save(): Promise<this> {\n this.validateAmounts();\n\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n\n // SENT *and* CONFIRMED both require a backendTxRef (S5 audit #1390 round 4):\n // a confirmed payout that carries no outgoing-transaction reference is\n // untraceable and indistinguishable from a forged confirmation. Round 3\n // only required it for SENT, leaving a raw CONFIRMED-with-no-txref hole.\n if (\n (this.status === PayoutStatus.SENT ||\n this.status === PayoutStatus.CONFIRMED) &&\n !this.backendTxRef\n ) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: a ${this.status} payout requires a ` +\n 'backendTxRef (use markSent()).',\n );\n }\n\n // CONFIRMED is only ever reachable from SENT (codex HIGH#4 / HIGH#3): an\n // existing row's transition is checked by assertStatusTransition, but a\n // brand-new row (no prior) would otherwise be allowed to start CONFIRMED.\n // A confirmation means the chain/gateway acknowledged a previously-sent tx,\n // so it can never be the genesis state.\n if (this.status === PayoutStatus.CONFIRMED && prior !== PayoutStatus.SENT) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: CONFIRMED is only reachable from SENT ` +\n '(use markConfirmed() after markSent()).',\n );\n }\n\n await this.assertCappedBySourcePayment();\n\n const result = (await super.save()) as this;\n loadedPayoutStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status from the database (S5 audit #1390\n * round 4). The {@link loadedPayoutStatus} WeakMap is only populated when\n * {@link initialize} hydrated the row, so a `create({ id: <existing>,\n * _skipLoad: true })` upsert produces an instance with a missing WeakMap\n * entry — trusting it would treat the write as a brand-new row and skip the\n * transition + CONFIRMED-genesis guards. Reading the persisted row directly\n * makes a create-onto-existing behave as an update. `undefined` means no row\n * exists (genuinely new).\n */\n private async resolvePriorStatus(): Promise<PayoutStatus | undefined> {\n if (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 PayoutStatus;\n }\n } catch {\n // DB not ready — fall through to the in-memory record.\n }\n }\n return loadedPayoutStatus.get(this);\n }\n\n /**\n * Reject an illegal status flip on an existing row. Brand-new payouts (no\n * prior) may start in any status — collection imports and query fixtures\n * legitimately seed SENT / CONFIRMED rows — but advancing a *persisted* row\n * is constrained to the legal edges the helpers enforce, so a raw update\n * can't skip `pending → confirmed` or revive a terminal CONFIRMED.\n */\n private assertStatusTransition(prior: PayoutStatus | undefined): void {\n if (prior === undefined) return; // new row\n if (prior === this.status) return; // no-op re-save\n const allowed = PAYOUT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Payout ${this.id}: illegal status transition '${prior}' → ` +\n `'${this.status}'. Use the guarded helpers (markSent / markConfirmed ` +\n '/ markFailed / resetFromFailed).',\n );\n }\n }\n\n /**\n * Cap `grossAmount` by the source {@link Payment}'s settled funds. A payout\n * that remits more than the Payment that funded it brought in is money the\n * operator never received.\n *\n * HARD requirement (S5 audit #1390 round 4, codex HIGH#3): the source Payment\n * MUST resolve. Round 3 made the cap best-effort — a `paymentId` that didn't\n * resolve silently skipped the cap, so a caller could remit an arbitrary\n * `grossAmount` simply by pointing at a non-existent (or empty) payment.\n * A payout's whole purpose is to remit funds that *arrived* via a Payment;\n * without a resolvable source there is no funded amount to cap against, so we\n * reject rather than skip. `PayoutCollection.createFromPayment()` always wires\n * a real `paymentId`, so this never blocks the supported creation path.\n */\n private async assertCappedBySourcePayment(): Promise<void> {\n if (!this.paymentId) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: a paymentId is required — a payout must ` +\n 'reference the source Payment that funded it (use createFromPayment()).',\n );\n }\n\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await (PaymentCollection as any).create(this.options);\n const payment = await payments.get({ id: this.paymentId });\n if (!payment) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: referenced source Payment ` +\n `'${this.paymentId}' does not exist — refusing to remit against a ` +\n 'missing payment (the gross-amount cap cannot be enforced otherwise).',\n );\n }\n\n // Cap against the payment's funds. Prefer the native-rail figure when\n // present (volatile-currency rails), otherwise the settlement amount —\n // mirrors the PaymentIntent reconciliation convention.\n const settled =\n typeof payment.nativeAmount === 'number' && payment.nativeAmount > 0\n ? payment.nativeAmount\n : payment.amount;\n if (this.grossAmount - settled > PAYOUT_EPSILON) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: grossAmount ${this.grossAmount} exceeds the ` +\n `source Payment '${this.paymentId}' funded amount ${settled}.`,\n );\n }\n }\n\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default Payout;\n","/**\n * PayoutCollection — collection manager for {@link Payout} rows.\n *\n * Adds helpers for the common operator-side query shapes: payouts for\n * a specific vendor, payouts derived from a specific source payment,\n * status-bucketed lookups for the operator dashboard, and a\n * `createFromPayment` convenience that wires up the source-payment\n * link in one call.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport type { Payment } from '../models/Payment.js';\nimport { Payout } from '../models/Payout.js';\nimport { PayoutStatus } from '../types/index.js';\n\nexport interface CreatePayoutFromPaymentArgs {\n /** Source Payment row */\n payment: Payment;\n /** Destination vendor id */\n vendorId: string;\n /** Operator fee in the payment's native currency */\n operatorFee: number;\n /** Backend that will send the payout — defaults to the payment's */\n backendId?: string;\n /** Optional initial notes */\n notes?: string;\n}\n\nexport class PayoutCollection extends SmrtCollection<Payout> {\n static readonly _itemClass = Payout;\n\n /**\n * Build a `PENDING` payout linked to a source payment. The gross\n * defaults to the payment's `nativeAmount` (falling back to\n * `amount` if no backend rail was recorded), the currency defaults\n * to the payment's `nativeCurrency` (falling back to `currency`),\n * and `supplierNet` is computed as `gross - operatorFee`.\n *\n * The returned instance is initialized but not persisted. The caller\n * is still responsible for `await payout.save()` — keeping create /\n * save as separate steps lets callers stitch the payout into an\n * application-level transaction.\n */\n async createFromPayment(args: CreatePayoutFromPaymentArgs): Promise<Payout> {\n const { payment, vendorId, operatorFee } = args;\n const grossAmount = payment.nativeAmount || payment.amount || 0;\n const currency = payment.nativeCurrency || payment.currency || '';\n const backendId = args.backendId ?? payment.backendId ?? '';\n const payout = new Payout({\n ai: this.options.ai,\n db: this.db,\n _skipLoad: true,\n // Inherit the source payment's tenant. Payout is @TenantScoped, so when\n // this runs outside an active tenant context (e.g. a background payout\n // job iterating Payment rows), the interceptor's auto-populate can't\n // stamp a tenant — without carrying it explicitly the remittance would\n // save as global/null and drop out of tenant-filtered payout queries.\n tenantId: payment.tenantId,\n paymentId: payment.id ?? '',\n vendorId,\n grossAmount,\n operatorFee,\n supplierNet: grossAmount - operatorFee,\n currency,\n backendId,\n status: PayoutStatus.PENDING,\n notes: args.notes ?? '',\n });\n await payout.initialize();\n return payout;\n }\n\n async findByPayment(paymentId: string): Promise<Payout[]> {\n return this.list({\n where: { paymentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByVendor(vendorId: string): Promise<Payout[]> {\n return this.list({\n where: { vendorId },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByStatus(status: PayoutStatus): Promise<Payout[]> {\n return this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n async findPending(): Promise<Payout[]> {\n return this.findByStatus(PayoutStatus.PENDING);\n }\n\n async findFailed(): Promise<Payout[]> {\n return this.findByStatus(PayoutStatus.FAILED);\n }\n\n async findByBackendTxRef(backendTxRef: string): Promise<Payout | null> {\n if (!backendTxRef) return null;\n const results = await this.list({\n where: { backendTxRef },\n limit: 1,\n });\n return results[0] ?? null;\n }\n\n /**\n * Total funds delivered to a vendor in confirmed payouts. Useful for\n * a vendor-balance widget on the operator dashboard.\n */\n async getConfirmedTotalForVendor(vendorId: string): Promise<number> {\n const confirmed = await this.list({\n where: { vendorId, status: PayoutStatus.CONFIRMED },\n });\n return confirmed.reduce((sum, p) => sum + p.supplierNet, 0);\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n async findByTenant(tenantId: string): Promise<Payout[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payouts (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 async findGlobal(): Promise<Payout[]> {\n return queryGlobal<Payout>(this);\n }\n\n /**\n * Find payouts for a tenant including global payouts.\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 async findWithGlobals(tenantId: string): Promise<Payout[]> {\n return queryWithGlobals<Payout>(this, tenantId, 'Payout.findWithGlobals');\n }\n}\n","/**\n * VendorCollection - Collection manager for Vendor objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Vendor } from '../models/Vendor.js';\nimport { VendorStatus } from '../types/index.js';\n\nexport class VendorCollection extends SmrtCollection<Vendor> {\n static readonly _itemClass = Vendor;\n\n /**\n * Find vendors by profile ID\n *\n * @param profileId - Profile ID from smrt-profiles\n * @returns Array of vendors linked to this profile\n */\n async findByProfile(profileId: string): Promise<Vendor[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active vendors\n *\n * @returns Array of active vendors\n */\n async findActive(): Promise<Vendor[]> {\n return await this.list({\n where: { status: VendorStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find vendors by status\n *\n * @param status - Vendor status\n * @returns Array of vendors\n */\n async findByStatus(status: VendorStatus): Promise<Vendor[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Get or create a vendor for a profile\n *\n * @param profileId - Profile ID\n * @param defaults - Default values if creating\n * @returns Vendor\n */\n async getOrCreateForProfile(\n profileId: string,\n defaults: Partial<{\n leadTimeDays: number;\n minimumOrderAmount: number;\n paymentTerms: string;\n }> = {},\n ): Promise<Vendor> {\n const existing = await this.findByProfile(profileId);\n if (existing.length > 0) {\n return existing[0];\n }\n\n const vendor = await this.create({\n profileId,\n leadTimeDays: defaults.leadTimeDays ?? 0,\n minimumOrderAmount: defaults.minimumOrderAmount ?? 0,\n paymentTerms: defaults.paymentTerms ?? '',\n status: VendorStatus.ACTIVE,\n });\n await vendor.save();\n return vendor;\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all vendors belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of vendors for the tenant\n */\n async findByTenant(tenantId: string): Promise<Vendor[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global vendors (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 vendors\n */\n async findGlobal(): Promise<Vendor[]> {\n return queryGlobal<Vendor>(this);\n }\n\n /**\n * Find vendors for a tenant including global vendors.\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 vendors\n */\n async findWithGlobals(tenantId: string): Promise<Vendor[]> {\n return queryWithGlobals<Vendor>(this, tenantId, 'Vendor.findWithGlobals');\n }\n}\n"],"names":["CustomerStatus","CustomerType","VendorStatus","ContractType","ContractStatus","FulfillmentType","FulfillmentStatus","PaymentMethod","PaymentStatus","PayoutStatus","PaymentIntentStatus","InvoiceStatus","__decorateClass","tenantId","ContractLineItemCollection","FulfillmentCollection","FulfillmentLineItemCollection","InvoiceLineItemCollection","PaymentAllocationCollection","field","seq","PaymentCollection","InvoiceCollection"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/__smrt-register__.ts","../src/types/index.ts","../src/models/Customer.ts","../src/models/Vendor.ts","../src/models/Contract.ts","../src/collections/ContractCollection.ts","../src/models/ContractLineItem.ts","../src/collections/ContractLineItemCollection.ts","../src/collections/CustomerCollection.ts","../src/models/Fulfillment.ts","../src/collections/FulfillmentCollection.ts","../src/models/FulfillmentLineItem.ts","../src/collections/FulfillmentLineItemCollection.ts","../src/models/Invoice.ts","../src/collections/InvoiceCollection.ts","../src/models/InvoiceLineItem.ts","../src/collections/InvoiceLineItemCollection.ts","../src/models/PaymentAllocation.ts","../src/collections/PaymentAllocationCollection.ts","../src/models/Payment.ts","../src/collections/PaymentCollection.ts","../src/models/PaymentIntent.ts","../src/collections/PaymentIntentCollection.ts","../src/models/Payout.ts","../src/collections/PayoutCollection.ts","../src/collections/VendorCollection.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// `new URL('./manifest.json', import.meta.url)` resolves at runtime to the\n// manifest sitting next to this module's compiled output. Vite warns at build\n// time that it cannot pre-resolve the URL; that is the intended behavior —\n// the URL must resolve to dist/manifest.json at runtime, not be inlined.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Commerce package type definitions\n * @packageDocumentation\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\n\n// ============================================================================\n// Customer/Vendor Types\n// ============================================================================\n\nexport enum CustomerStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n SUSPENDED = 'suspended',\n}\n\n/**\n * Customer classification — drives channel access, pricing tier, and credit terms.\n *\n * - DTC: direct-to-consumer (end shopper). Pays via storefront checkout.\n * - WHOLESALE: B2B buyer with negotiated terms (NET-30, line sheets, etc.).\n * - RETAIL: in-store walk-in / point-of-sale customer.\n */\nexport enum CustomerType {\n DTC = 'dtc',\n WHOLESALE = 'wholesale',\n RETAIL = 'retail',\n}\n\nexport enum VendorStatus {\n ACTIVE = 'active',\n INACTIVE = 'inactive',\n SUSPENDED = 'suspended',\n}\n\n// ============================================================================\n// Contract Types\n// ============================================================================\n\nexport enum ContractType {\n ESTIMATE = 'estimate',\n ORDER = 'order',\n LEASE = 'lease',\n AGREEMENT = 'agreement',\n PURCHASE_ORDER = 'purchase_order',\n WHOLESALE_ORDER = 'wholesale_order',\n PRODUCTION_ORDER = 'production_order',\n CART = 'cart',\n /**\n * Industry-neutral licensing primitive. The `LicenseSale` STI subtype\n * carries an immutable rights snapshot, a licensee email, optional\n * legal-entity / jurisdiction fields, and a signed-PDF reference.\n * Any application that sells rights for a fee (stock media,\n * music licensing, code-asset marketplace, license keys, etc.)\n * uses this subtype.\n */\n LICENSE_SALE = 'license_sale',\n}\n\nexport enum ContractStatus {\n DRAFT = 'draft',\n SENT = 'sent',\n ACCEPTED = 'accepted',\n DECLINED = 'declined',\n COMPLETED = 'completed',\n CANCELLED = 'cancelled',\n}\n\n// ============================================================================\n// Fulfillment Types\n// ============================================================================\n\nexport enum FulfillmentType {\n SHIPMENT = 'shipment',\n DELIVERY = 'delivery',\n PICKUP = 'pickup',\n DIGITAL = 'digital',\n SERVICE = 'service',\n}\n\nexport enum FulfillmentStatus {\n PENDING = 'pending',\n PROCESSING = 'processing',\n SHIPPED = 'shipped',\n DELIVERED = 'delivered',\n CANCELLED = 'cancelled',\n}\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\nexport enum PaymentMethod {\n CASH = 'cash',\n CHECK = 'check',\n CREDIT_CARD = 'credit_card',\n BANK_TRANSFER = 'bank_transfer',\n CRYPTO = 'crypto',\n OTHER = 'other',\n}\n\nexport enum PaymentStatus {\n PENDING = 'pending',\n COMPLETED = 'completed',\n FAILED = 'failed',\n REFUNDED = 'refunded',\n CANCELLED = 'cancelled',\n}\n\n// ============================================================================\n// Payout Types\n// ============================================================================\n\n/**\n * Status machine for {@link Payout}.\n *\n * `pending → sent → confirmed → failed`. `failed` is terminal but the\n * model exposes a dedicated `resetFromFailed()` so an operator can\n * deliberately requeue a payout after fixing the underlying problem;\n * the reset moves the row back to `pending` and clears the failure\n * metadata.\n *\n * Skipping intermediate states is rejected — a `pending` payout cannot\n * jump straight to `confirmed`, and a `sent` payout cannot regress to\n * `pending` without going through `resetFromFailed()` first.\n */\nexport enum PayoutStatus {\n PENDING = 'pending',\n SENT = 'sent',\n CONFIRMED = 'confirmed',\n FAILED = 'failed',\n}\n\n// ============================================================================\n// PaymentIntent Types\n// ============================================================================\n\n/**\n * Status machine for {@link PaymentIntent}.\n *\n * `awaiting_payment → paid → (issued | retired)`, with `expired` and\n * `cancelled` as alternate terminal states reachable from\n * `awaiting_payment`.\n *\n * - `AWAITING_PAYMENT` — open quote, accepting any of the listed\n * payment options.\n * - `PAID` — one option was satisfied by an incoming payment. The\n * other options are implicitly retired; later inbound funds to a\n * retired option must be flagged for refund by the consumer.\n * - `ISSUED` — downstream rights / contract / fulfillment have been\n * created and linked. Terminal happy-path state.\n * - `RETIRED` — paid but the satisfaction was reversed (refund,\n * chargeback, etc.). Terminal.\n * - `EXPIRED` — the USD-price-lock window passed without payment.\n * Terminal.\n * - `CANCELLED` — the buyer or the system explicitly cancelled the\n * intent before it was paid. Terminal.\n */\nexport enum PaymentIntentStatus {\n AWAITING_PAYMENT = 'awaiting_payment',\n PAID = 'paid',\n ISSUED = 'issued',\n RETIRED = 'retired',\n EXPIRED = 'expired',\n CANCELLED = 'cancelled',\n}\n\n/**\n * One way for a {@link PaymentIntent} to be satisfied. The intent lists\n * an array of these; satisfying any one of them transitions the intent\n * to `PAID` and implicitly retires the rest.\n *\n * Industry-neutral: a marketplace that accepts USDC-on-Base and BTC\n * lists two options; a SaaS billing flow that accepts Stripe and\n * PayPal lists two; a kiosk that accepts only one rail lists one.\n */\nexport interface PaymentOption {\n /**\n * Stable id of the `PaymentBackend` adapter that fulfills this\n * option — must match a `backendId` the consumer's payment-routing\n * layer recognises. Examples: `base-usdc`, `solana-usdc`, `btc`,\n * `stripe`, `paypal`.\n */\n backendId: string;\n /**\n * Payout-rail-qualified currency code, e.g. `USDC-base`, `BTC`,\n * `USD-stripe`. The same convention used by\n * `Vendor.payoutAddresses` and `Payment.nativeCurrency`.\n */\n currency: string;\n /**\n * Optional chain identifier for blockchain-backed options (`base`,\n * `ethereum`, `solana`, ...). Pure-fiat options leave this empty.\n */\n chain?: string;\n /**\n * Destination address / account id the buyer should send funds to\n * for this option (EVM address, BTC address, Stripe payment-intent\n * id, etc.).\n */\n payTo: string;\n /**\n * Amount denominated in `currency`. Stored at decimal precision —\n * a USDC option might be `199.0`, a BTC option `0.00713`.\n */\n nativeAmount: number;\n /**\n * Optional on-chain memo / payment reference (Solana memo, BTC OP_RETURN\n * tag, Stripe metadata key). Some backends use this to disambiguate\n * which intent an inbound payment satisfies.\n */\n memo?: string;\n /**\n * Whether this option supports the x402 HTTP-402 payment-required\n * flow. Consumers that build agent-driven flows use this flag to\n * filter the offered options.\n */\n x402Capable?: boolean;\n /**\n * Optional per-option expiry — when a single rail's quote becomes\n * stale earlier than the intent-wide price-lock window (e.g. a\n * volatile-currency option that re-quotes more aggressively). ISO\n * 8601 string. Empty / missing means \"inherit the intent's\n * `priceLockExpiresAt`\".\n */\n expiresAt?: string;\n}\n\n// ============================================================================\n// Invoice Types\n// ============================================================================\n\nexport enum InvoiceStatus {\n DRAFT = 'draft',\n SENT = 'sent',\n VIEWED = 'viewed',\n PARTIAL = 'partial',\n PAID = 'paid',\n OVERDUE = 'overdue',\n CANCELLED = 'cancelled',\n WRITTEN_OFF = 'written_off',\n}\n\n// ============================================================================\n// Common Interfaces\n// ============================================================================\n\n/**\n * Address structure used for billing and shipping\n */\nexport interface Address {\n street1?: string;\n street2?: string;\n city?: string;\n state?: string;\n postalCode?: string;\n country?: string;\n}\n\n/**\n * Options for recording a payment with ledger integration\n */\nexport interface RecordPaymentOptions {\n /** The ledger to record the journal entry in */\n ledgerId: string;\n /** Account ID for receivables (credit side) */\n receivablesAccountId: string;\n /** Account ID for cash/bank (debit side) */\n cashAccountId: string;\n}\n\n/**\n * Options for recognizing revenue on an invoice\n */\nexport interface RecognizeRevenueOptions {\n /** Account ID for accounts receivable (debit side) */\n arAccountId: string;\n /** Account ID for revenue (credit side) */\n revenueAccountId: string;\n /** Account ID for tax payable (credit side, optional) */\n taxAccountId?: string;\n}\n\n// ============================================================================\n// Accounting-provider sync shapes\n// ============================================================================\n\n/**\n * A single line item in the format expected by `@happyvertical/accounting`\n * providers (QBO, Stripe, …). Mirrors the object built by\n * {@link InvoiceLineItem.toAccountingLineItem}. The SDK isn't a dependency of\n * this package, so the shape is declared locally rather than imported.\n */\nexport interface AccountingLineItemInput {\n description: string;\n sku?: string;\n quantity: number;\n unitPrice: number;\n discount?: number;\n taxRate?: number;\n amount: number;\n periodStart?: Date;\n periodEnd?: Date;\n}\n\n/**\n * An invoice in the format expected by `@happyvertical/accounting` providers.\n * Mirrors the object built by {@link Invoice.toAccountingInput}.\n */\nexport interface AccountingInvoiceInput {\n id: string;\n externalId?: string;\n invoiceNumber: string;\n customerId: string;\n customerExternalId?: string;\n issueDate: Date;\n dueDate: Date;\n lineItems: AccountingLineItemInput[];\n subtotal: number;\n taxAmount: number;\n totalAmount: number;\n currency: string;\n reference?: string;\n memo?: string;\n}\n\n// ============================================================================\n// Model constructor option interfaces\n//\n// Each model's `constructor(options)` accepts a partial bag of its own fields.\n// Declaring a dedicated `XxxOptions extends SmrtObjectOptions` interface keeps\n// the constructors strongly typed (no `any`) while still allowing the\n// framework's base options (db, ai, fs, _className, …) through.\n// ============================================================================\n\n/**\n * Constructor options for {@link Customer}.\n */\nexport interface CustomerOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n profileId?: string;\n creditLimit?: number;\n paymentTerms?: string;\n taxExempt?: boolean;\n taxId?: string;\n defaultShippingAddress?: Address;\n defaultBillingAddress?: Address;\n status?: CustomerStatus;\n customerType?: CustomerType | null;\n notes?: string;\n}\n\n/**\n * Constructor options for {@link Vendor}.\n */\nexport interface VendorOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n profileId?: string;\n leadTimeDays?: number;\n minimumOrderAmount?: number;\n paymentTerms?: string;\n currency?: string;\n defaultContactEmail?: string;\n defaultContactPhone?: string;\n status?: VendorStatus;\n notes?: string;\n payoutAddresses?: Record<string, string> | string | null;\n}\n\n/**\n * Constructor options for {@link Contract} and its STI subtypes.\n */\nexport interface ContractOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n contractType?: ContractType;\n status?: ContractStatus;\n customerId?: string;\n vendorId?: string;\n subtotal?: number;\n taxAmount?: number;\n totalAmount?: number;\n currency?: string;\n issueDate?: Date;\n dueDate?: Date | null;\n expiryDate?: Date | null;\n reference?: string;\n notes?: string;\n terms?: string;\n channelId?: string;\n}\n\n/**\n * Constructor options for {@link LicenseSale} (Contract STI subtype). Extends\n * {@link ContractOptions} with the licensing-specific meta fields.\n */\nexport interface LicenseSaleOptions extends ContractOptions {\n skuId?: string;\n paymentId?: string;\n licenseeEmail?: string;\n licenseeLegalEntity?: string;\n licenseeJurisdiction?: string;\n rightsMedium?: string;\n rightsDistributionScope?: string;\n rightsExclusivity?: string;\n rightsDuration?: string;\n rightsTerritory?: string;\n rightsSublicensing?: boolean;\n rightsDerivatives?: boolean;\n pdfUrl?: string;\n pdfHash?: string;\n onChainHashRegistryRef?: string;\n}\n\n/**\n * Constructor options for {@link ContractLineItem}.\n */\nexport interface ContractLineItemOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n contractId?: string;\n description?: string;\n quantity?: number;\n unitPrice?: number;\n discount?: number;\n taxRate?: number;\n amount?: number;\n productId?: string;\n sku?: string;\n startDate?: Date | null;\n endDate?: Date | null;\n billingPeriod?: string;\n sortOrder?: number;\n}\n\n/**\n * Constructor options for {@link Invoice}.\n */\nexport interface InvoiceOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n customerId?: string;\n contractId?: string;\n invoiceNumber?: string;\n reference?: string;\n issueDate?: Date;\n dueDate?: Date;\n paidDate?: Date | null;\n subtotal?: number;\n taxAmount?: number;\n totalAmount?: number;\n amountPaid?: number;\n currency?: string;\n status?: InvoiceStatus;\n arJournalId?: string;\n revenueJournalId?: string;\n externalId?: string;\n customerExternalId?: string;\n externalProvider?: string;\n syncedAt?: Date | null;\n sentAt?: Date | null;\n viewedAt?: Date | null;\n remindersSent?: number;\n lastReminderAt?: Date | null;\n notes?: string;\n customerNotes?: string;\n terms?: string;\n}\n\n/**\n * Constructor options for {@link InvoiceLineItem}.\n */\nexport interface InvoiceLineItemOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n invoiceId?: string;\n description?: string;\n sku?: string;\n quantity?: number;\n unitPrice?: number;\n discount?: number;\n taxRate?: number;\n amount?: number;\n sourceType?: string;\n sourceId?: string;\n periodStart?: Date | null;\n periodEnd?: Date | null;\n revenueAccountId?: string;\n sortOrder?: number;\n}\n\n/**\n * Constructor options for {@link Fulfillment}.\n */\nexport interface FulfillmentOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n contractId?: string;\n fulfillmentType?: FulfillmentType;\n status?: FulfillmentStatus;\n trackingNumber?: string;\n carrier?: string;\n shippingAddress?: Address;\n shippedAt?: Date | null;\n deliveredAt?: Date | null;\n estimatedDelivery?: Date | null;\n notes?: string;\n}\n\n/**\n * Constructor options for {@link FulfillmentLineItem}.\n */\nexport interface FulfillmentLineItemOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n fulfillmentId?: string;\n contractLineItemId?: string;\n quantityFulfilled?: number;\n notes?: string;\n}\n\n/**\n * Constructor options for {@link Payment}.\n */\nexport interface PaymentOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n contractId?: string;\n customerId?: string;\n amount?: number;\n currency?: string;\n method?: PaymentMethod;\n status?: PaymentStatus;\n transactionId?: string;\n reference?: string;\n journalId?: string;\n paidAt?: Date | null;\n notes?: string;\n externalId?: string;\n externalProvider?: string;\n syncedAt?: Date | null;\n backendId?: string;\n backendTxRef?: string;\n nativeAmount?: number;\n nativeCurrency?: string;\n usdAtQuote?: number;\n usdAtConfirmation?: number;\n}\n\n/**\n * Constructor options for {@link PaymentAllocation}.\n */\nexport interface PaymentAllocationOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n paymentId?: string;\n invoiceId?: string;\n amount?: number;\n allocatedAt?: Date;\n allocatedBy?: string;\n notes?: string;\n}\n\n/**\n * Constructor options for {@link PaymentIntent}.\n */\nexport interface PaymentIntentOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n skuId?: string;\n offeringRef?: string;\n licenseeEmail?: string;\n customerId?: string;\n paymentOptions?: PaymentOption[] | string;\n usdPriceLocked?: number;\n priceLockWindowMs?: number;\n priceLockExpiresAt?: Date | number | string | null;\n status?: PaymentIntentStatus;\n idempotencyKey?: string;\n paidOptionBackendId?: string;\n paymentId?: string;\n paidAt?: Date | number | string | null;\n issuedAt?: Date | number | string | null;\n expiredAt?: Date | number | string | null;\n cancelledAt?: Date | number | string | null;\n retiredAt?: Date | number | string | null;\n notes?: string;\n}\n\n/**\n * Constructor options for {@link Payout}.\n */\nexport interface PayoutOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n paymentId?: string;\n vendorId?: string;\n grossAmount?: number;\n operatorFee?: number;\n supplierNet?: number;\n currency?: string;\n backendId?: string;\n backendTxRef?: string;\n status?: PayoutStatus;\n sentAt?: Date | number | string | null;\n confirmedAt?: Date | number | string | null;\n failedAt?: Date | number | string | null;\n failureReason?: string;\n notes?: string;\n}\n","/**\n * Customer model - links to Profile with customer-specific data\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type Address,\n type CustomerOptions,\n CustomerStatus,\n CustomerType,\n} from '../types/index.js';\n\n/**\n * Customer represents the customer role for a Profile.\n *\n * One Profile can have multiple Customer records (e.g., different business contexts).\n * Customer stores customer-specific data like credit limits and payment terms.\n *\n * @example\n * ```typescript\n * const customer = await customers.create({\n * profileId: 'profile-uuid',\n * creditLimit: 10000,\n * paymentTerms: 'Net 30'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Customer extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global customers\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Reference to smrt-profiles Profile\n * Plain string for cross-package reference\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId: string = '';\n\n /**\n * Maximum credit extended to this customer\n */\n creditLimit: number = 0.0;\n\n /**\n * Payment terms (e.g., \"Net 30\", \"Due on receipt\", \"2/10 Net 30\")\n */\n paymentTerms: string = '';\n\n /**\n * Whether customer is exempt from tax\n */\n taxExempt: boolean = false;\n\n /**\n * Tax identification number\n *\n * Sensitive (#1540): PII excluded from generated API/MCP responses and\n * rejected as a `where` filter key.\n */\n @field({ sensitive: true })\n taxId: string = '';\n\n /**\n * Default shipping address\n */\n defaultShippingAddress: Address = {};\n\n /**\n * Default billing address\n */\n defaultBillingAddress: Address = {};\n\n /**\n * Customer status\n */\n status: CustomerStatus = CustomerStatus.ACTIVE;\n\n /**\n * Customer classification — DTC, wholesale, or retail.\n *\n * Drives channel access (wholesale portal vs. storefront vs. POS), default\n * price tier, and credit terms. Defaults to DTC for backwards compatibility.\n */\n customerType: CustomerType = CustomerType.DTC;\n\n /**\n * Internal notes about this customer\n */\n notes: string = '';\n\n constructor(options: CustomerOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.creditLimit !== undefined)\n this.creditLimit = options.creditLimit;\n if (options.paymentTerms !== undefined)\n this.paymentTerms = options.paymentTerms;\n if (options.taxExempt !== undefined) this.taxExempt = options.taxExempt;\n if (options.taxId !== undefined) this.taxId = options.taxId;\n if (options.defaultShippingAddress !== undefined)\n this.defaultShippingAddress = options.defaultShippingAddress;\n if (options.defaultBillingAddress !== undefined)\n this.defaultBillingAddress = options.defaultBillingAddress;\n if (options.status !== undefined) this.status = options.status;\n // Treat `null` the same as `undefined` here. Customers loaded from\n // rows written before the `customer_type` column existed will\n // hydrate with the field as SQL `NULL`; without this guard the\n // SmrtObject loader would overwrite the `CustomerType.DTC`\n // initializer with `null`, breaking the documented\n // backwards-compatible default. Explicit string values (including\n // any future custom flavour) still take precedence.\n if (options.customerType !== undefined && options.customerType !== null) {\n this.customerType = options.customerType;\n }\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Convenience predicate: is this a wholesale customer?\n */\n isWholesale(): boolean {\n return this.customerType === CustomerType.WHOLESALE;\n }\n\n /**\n * Check if customer is active\n */\n isActive(): boolean {\n return this.status === CustomerStatus.ACTIVE;\n }\n\n /**\n * Check if customer has available credit\n */\n hasAvailableCredit(amount: number): boolean {\n // In a full implementation, this would check against outstanding balances\n return this.creditLimit >= amount;\n }\n\n /**\n * Get the profile ID for external lookup.\n *\n * To get the actual Profile object, use the ProfileCollection from smrt-profiles:\n * ```typescript\n * const profiles = await ProfileCollection.create(options);\n * const profile = await profiles.get({ id: customer.profileId });\n * ```\n *\n * @returns The profile ID or empty string if not set\n */\n getProfileId(): string {\n return this.profileId;\n }\n}\n\nexport default Customer;\n","/**\n * Vendor model - links to Profile with vendor-specific data\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { type VendorOptions, VendorStatus } from '../types/index.js';\n\n/**\n * Vendor represents the vendor/supplier role for a Profile.\n *\n * One Profile can have both Customer and Vendor records - e.g., a company\n * you both buy from and sell to.\n *\n * @example\n * ```typescript\n * const vendor = await vendors.create({\n * profileId: 'profile-uuid',\n * leadTimeDays: 14,\n * minimumOrderAmount: 500,\n * paymentTerms: 'Net 60'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Vendor extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global vendors\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Reference to smrt-profiles Profile\n * Plain string for cross-package reference\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId: string = '';\n\n /**\n * Typical lead time in days for orders\n */\n leadTimeDays: number = 0;\n\n /**\n * Minimum order amount required\n */\n minimumOrderAmount: number = 0.0;\n\n /**\n * Payment terms for this vendor (e.g., \"Net 60\")\n */\n paymentTerms: string = '';\n\n /**\n * Default currency for transactions with this vendor\n */\n currency: string = 'USD';\n\n /**\n * Default contact email for orders\n */\n defaultContactEmail: string = '';\n\n /**\n * Default contact phone for orders\n */\n defaultContactPhone: string = '';\n\n /**\n * Vendor status\n */\n status: VendorStatus = VendorStatus.ACTIVE;\n\n /**\n * Internal notes about this vendor\n */\n notes: string = '';\n\n /**\n * Per-currency payout destinations for this vendor.\n *\n * Stored as a JSON column. Keys are payout-rail-qualified currency codes —\n * the code must encode both the asset and the rail (e.g. `USDC-base`,\n * `USDC-solana`, `BTC`, `USD-stripe`, `USD-wire`). Values are whatever the\n * receiving rail needs as a destination (EVM address, BTC address, Stripe\n * Connect account id, IBAN, etc.).\n *\n * MVP shape: a flat map on the vendor row. A vendor with no entry for a\n * given currency cannot be paid in that currency — filtering is the\n * consumer's responsibility (the upstream `PaymentIntent` builder filters\n * `paymentOptions` at quote time against this map).\n *\n * Use {@link getPayoutAddress} for a `Map.get`-style lookup that returns\n * `undefined` for missing entries; direct property access is also fine.\n *\n * Sensitive (#1540): payout destinations are excluded from generated API/MCP\n * responses and rejected as a `where` filter key.\n */\n @field({ sensitive: true })\n payoutAddresses: Record<string, string> = {};\n\n constructor(options: VendorOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.leadTimeDays !== undefined)\n this.leadTimeDays = options.leadTimeDays;\n if (options.minimumOrderAmount !== undefined)\n this.minimumOrderAmount = options.minimumOrderAmount;\n if (options.paymentTerms !== undefined)\n this.paymentTerms = options.paymentTerms;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.defaultContactEmail !== undefined)\n this.defaultContactEmail = options.defaultContactEmail;\n if (options.defaultContactPhone !== undefined)\n this.defaultContactPhone = options.defaultContactPhone;\n if (options.status !== undefined) this.status = options.status;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.payoutAddresses !== undefined) {\n this.payoutAddresses = Vendor.normalizePayoutAddresses(\n options.payoutAddresses,\n );\n }\n }\n\n /**\n * The framework's {@link SmrtObject.initialize} re-applies `options` keys on\n * top of constructor-set values so option data always wins over field\n * initializers. That means our constructor's normalization runs *before*\n * `initializePropertiesFromOptions` overwrites `payoutAddresses` with the\n * raw cloned value. Re-normalize once super has finished so consumers\n * who pass a string, a partially-typed map, or `null` end up with a\n * clean `Record<string, string>` before any save / read happens.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.payoutAddresses = Vendor.normalizePayoutAddresses(\n this.payoutAddresses as unknown,\n );\n return this;\n }\n\n /**\n * Save with a final normalization pass so direct field assignments\n * (`vendor.payoutAddresses = somethingFunny`) can't smuggle non-string\n * values into the persisted row.\n */\n override async save(): Promise<this> {\n this.payoutAddresses = Vendor.normalizePayoutAddresses(\n this.payoutAddresses as unknown,\n );\n return super.save() as Promise<this>;\n }\n\n /**\n * Check if vendor is active\n */\n isActive(): boolean {\n return this.status === VendorStatus.ACTIVE;\n }\n\n /**\n * Check if order meets minimum requirements\n */\n meetsMinimumOrder(amount: number): boolean {\n return amount >= this.minimumOrderAmount;\n }\n\n /**\n * Get the profile ID for external lookup.\n *\n * To get the actual Profile object, use the ProfileCollection from smrt-profiles:\n * ```typescript\n * const profiles = await ProfileCollection.create(options);\n * const profile = await profiles.get({ id: vendor.profileId });\n * ```\n *\n * @returns The profile ID or empty string if not set\n */\n getProfileId(): string {\n return this.profileId;\n }\n\n /**\n * Look up the payout destination for a single currency. Returns\n * `undefined` if the vendor has no entry for that currency — caller's\n * job to decide whether that means \"skip the option\" or \"raise a\n * configuration error\". The lookup is case-sensitive; the contract is\n * that the currency code passed in matches exactly what was stored\n * (`USDC-base`, not `usdc-base`).\n */\n getPayoutAddress(currency: string): string | undefined {\n const map = this.payoutAddresses;\n if (!map || typeof map !== 'object') return undefined;\n const value = (map as Record<string, unknown>)[currency];\n return typeof value === 'string' ? value : undefined;\n }\n\n /**\n * Set the payout destination for a single currency. Mutates the live\n * map and returns `this` for chaining. Use {@link clearPayoutAddress}\n * to remove a single entry.\n */\n setPayoutAddress(currency: string, destination: string): this {\n this.payoutAddresses = {\n ...(this.payoutAddresses ?? {}),\n [currency]: destination,\n };\n return this;\n }\n\n /**\n * Remove a single currency entry from the payout map. No-op if the\n * currency was already absent.\n */\n clearPayoutAddress(currency: string): this {\n if (!this.payoutAddresses || !(currency in this.payoutAddresses)) {\n return this;\n }\n const next = { ...this.payoutAddresses };\n delete next[currency];\n this.payoutAddresses = next;\n return this;\n }\n\n /**\n * Normalize an input value (object, pre-serialized JSON string, or\n * `null`/`undefined`) into a plain `Record<string, string>`. Used by\n * the constructor so consumers can pass either shape into\n * `VendorCollection.create({ payoutAddresses: ... })`.\n *\n * Non-string values inside an input object are silently dropped — a\n * `Record<string, string>` invariant is what consumers downstream\n * (`PaymentIntent` builders, payout SDK adapters) rely on.\n */\n private static normalizePayoutAddresses(\n value: unknown,\n ): Record<string, string> {\n if (value == null) return {};\n let parsed: unknown = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value);\n } catch {\n return {};\n }\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {};\n }\n const out: Record<string, string> = {};\n for (const [key, v] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof v === 'string') out[key] = v;\n }\n return out;\n }\n}\n\nexport default Vendor;\n","/**\n * Contract model with STI for different contract types\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n type Meta,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type ContractOptions,\n ContractStatus,\n ContractType,\n type LicenseSaleOptions,\n} from '../types/index.js';\nimport { Customer } from './Customer.js';\nimport { Vendor } from './Vendor.js';\n\n/**\n * Legal status transitions for a Contract (and all its STI subtypes), keyed by\n * the prior persisted status. A status re-saved unchanged (no-op) and a\n * brand-new row are always permitted; this map governs *changes* only.\n *\n * Flow: DRAFT → SENT → (ACCEPTED | DECLINED); ACCEPTED → (COMPLETED |\n * CANCELLED). An estimate/order may also be cancelled directly from DRAFT/SENT.\n * DECLINED / COMPLETED / CANCELLED are terminal. This guards against raw\n * mass-assignment forcing, e.g., a DECLINED contract back to ACCEPTED or a\n * COMPLETED one back to DRAFT. See S5 audit #1390.\n */\nconst CONTRACT_STATUS_TRANSITIONS: Record<ContractStatus, ContractStatus[]> = {\n [ContractStatus.DRAFT]: [\n ContractStatus.SENT,\n ContractStatus.ACCEPTED,\n ContractStatus.DECLINED,\n ContractStatus.CANCELLED,\n ],\n [ContractStatus.SENT]: [\n ContractStatus.ACCEPTED,\n ContractStatus.DECLINED,\n ContractStatus.CANCELLED,\n ],\n [ContractStatus.ACCEPTED]: [\n ContractStatus.COMPLETED,\n ContractStatus.CANCELLED,\n ],\n [ContractStatus.DECLINED]: [],\n [ContractStatus.COMPLETED]: [],\n [ContractStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each Contract instance was loaded with,\n * for the save-time transition guard. WeakMap keeps it out of the schema and\n * GCs with the instance — same pattern as the other commerce models.\n */\nconst loadedContractStatus = new WeakMap<Contract, ContractStatus>();\n\n/**\n * Contract is the base class for all commercial agreements.\n *\n * Uses Single Table Inheritance (STI) to support different contract types:\n * - Estimate: Quote/proposal for customer\n * - Order: Customer purchase order\n * - Lease: Rental/lease agreement\n * - Agreement: Service/maintenance agreement\n * - PurchaseOrder: Order to vendor/supplier\n *\n * @example\n * ```typescript\n * // Create a customer order\n * const order = await contracts.create({\n * _meta_type: 'Order',\n * customerId: customer.id,\n * totalAmount: 1500.00,\n * status: ContractStatus.DRAFT\n * });\n *\n * // Create a purchase order to vendor\n * const po = await contracts.create({\n * _meta_type: 'PurchaseOrder',\n * vendorId: vendor.id,\n * totalAmount: 5000.00,\n * reference: 'PO-2024-001'\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Contract extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global contracts\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Contract type discriminator (STI)\n */\n contractType: ContractType = ContractType.ORDER;\n\n /**\n * Current status of the contract\n */\n status: ContractStatus = ContractStatus.DRAFT;\n\n /**\n * Customer ID (for sales contracts: Estimate, Order, Agreement)\n */\n @foreignKey(Customer)\n customerId: string = '';\n\n /**\n * Vendor ID (for purchase contracts: PurchaseOrder)\n */\n @foreignKey(Vendor)\n vendorId: string = '';\n\n /**\n * Subtotal before tax\n */\n subtotal: number = 0.0;\n\n /**\n * Tax amount\n */\n taxAmount: number = 0.0;\n\n /**\n * Total amount including tax\n */\n totalAmount: number = 0.0;\n\n /**\n * Currency code (ISO 4217)\n */\n currency: string = 'USD';\n\n /**\n * Date contract was issued\n */\n issueDate: Date = new Date();\n\n /**\n * Payment due date\n */\n dueDate: Date | null = null;\n\n /**\n * Expiry date (for estimates/quotes)\n */\n expiryDate: Date | null = null;\n\n /**\n * External reference number (PO number, quote number, etc.)\n */\n reference: string = '';\n\n /**\n * Internal notes\n */\n notes: string = '';\n\n /**\n * Terms and conditions\n */\n terms: string = '';\n\n /**\n * Sales channel that owns this contract.\n *\n * Plain string so consumers can model their own channels without an enum\n * change (e.g. `dtc-web`, `wholesale-b2b`, `pos-store-1`, `marketplace-faire`).\n * Empty string means \"unattributed / legacy\".\n */\n channelId: string = '';\n\n /**\n * STI discriminator field\n */\n static readonly _stiField = 'contractType';\n\n constructor(options: ContractOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractType !== undefined)\n this.contractType = options.contractType;\n if (options.status !== undefined) this.status = options.status;\n if (options.customerId !== undefined) this.customerId = options.customerId;\n if (options.vendorId !== undefined) this.vendorId = options.vendorId;\n if (options.subtotal !== undefined) this.subtotal = options.subtotal;\n if (options.taxAmount !== undefined) this.taxAmount = options.taxAmount;\n if (options.totalAmount !== undefined)\n this.totalAmount = options.totalAmount;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.issueDate !== undefined) this.issueDate = options.issueDate;\n if (options.dueDate !== undefined) this.dueDate = options.dueDate;\n if (options.expiryDate !== undefined) this.expiryDate = options.expiryDate;\n if (options.reference !== undefined) this.reference = options.reference;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.terms !== undefined) this.terms = options.terms;\n if (options.channelId !== undefined) this.channelId = options.channelId;\n }\n\n /**\n * Check if contract is in draft state\n */\n isDraft(): boolean {\n return this.status === ContractStatus.DRAFT;\n }\n\n /**\n * Check if contract is accepted\n */\n isAccepted(): boolean {\n return this.status === ContractStatus.ACCEPTED;\n }\n\n /**\n * Check if contract is completed\n */\n isCompleted(): boolean {\n return this.status === ContractStatus.COMPLETED;\n }\n\n /**\n * Check if contract is expired (for estimates)\n */\n isExpired(): boolean {\n if (!this.expiryDate) return false;\n return new Date() > this.expiryDate;\n }\n\n /**\n * Check if contract is overdue\n */\n isOverdue(): boolean {\n if (!this.dueDate) return false;\n if (this.status === ContractStatus.COMPLETED) return false;\n return new Date() > this.dueDate;\n }\n\n /**\n * Calculate and update totals from line items\n */\n async recalculateTotals(): Promise<void> {\n // This would typically query ContractLineItems and sum them\n // For now, just ensure totalAmount = subtotal + taxAmount\n this.totalAmount = this.subtotal + this.taxAmount;\n }\n\n /**\n * Capture the status the row was loaded with so the save-time transition\n * guard can reject illegal status flips made via raw field assignment\n * (mass-assignment on the generated update route, a stale caller, etc.).\n * Only persisted rows carry a prior status.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedContractStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Validate the status transition before persisting, then save. Inherited by\n * every STI subtype (Estimate/Order/Lease/.../LicenseSale), so a forged\n * `status` on any of them is caught here. LicenseSale layers its own\n * rights-immutability guard on top via `super.save()`. See S5 audit #1390.\n */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertContractStatusTransition(prior);\n const result = (await super.save()) as this;\n loadedContractStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Reject an illegal status flip done via raw assignment. No-op transitions\n * and brand-new rows are always allowed.\n */\n protected assertContractStatusTransition(\n prior: ContractStatus | undefined,\n ): 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 = CONTRACT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Contract ${this.reference || this.id}: illegal status transition ` +\n `'${prior}' → '${this.status}'.`,\n );\n }\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status (S5 audit #1390 round 5, codex\n * MEDIUM#2). The WeakMap is only populated when {@link initialize} loaded the\n * row from the DB; it is empty for an instance built via\n * `collection.create({ id: <existing>, _skipLoad: true })` — the upsert path\n * that lets a caller write onto an existing row without hydrating it. Trusting\n * an empty WeakMap there would treat the write as a brand-new row and skip the\n * transition guard entirely (a poisonable prior-state). Mirrors the\n * authoritative-prior-load applied to Payment/PaymentIntent/Invoice/Payout.\n *\n * So when this instance carries an `id`, read the persisted row straight from\n * the database and treat its `status` as the prior — a create-onto-existing is\n * an update. Only when no row exists in the DB (truly new) do we fall back to\n * the WeakMap (which is also empty then), i.e. `undefined` = genuinely new.\n * The raw row's `status` column is single-word (no snake_case transform).\n */\n protected async resolvePriorStatus(): Promise<ContractStatus | undefined> {\n if (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 ContractStatus;\n }\n } catch {\n // DB not ready / table absent — fall through to the in-memory record.\n }\n }\n return loadedContractStatus.get(this);\n }\n}\n\n// Every STI child below MUST repeat the `@TenantScoped` decoration. The\n// tenancy registry keys off the concrete className passed by the\n// collection, not the inheritance chain — `OrderCollection.list()` sends\n// `'Order'` to the interceptor, which then misses the `'Contract'`\n// registration and skips tenant auto-filter/auto-populate. Without an\n// explicit `@TenantScoped` on each subclass, saves end up with\n// `tenantId: null` (the row becomes global / invisible to tenant-scoped\n// queries) and lists return rows across tenants.\n\n/**\n * Estimate - Quote or proposal for a customer\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Estimate extends Contract {\n override contractType = ContractType.ESTIMATE;\n}\n\n/**\n * Order - Customer purchase order\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Order extends Contract {\n override contractType = ContractType.ORDER;\n}\n\n/**\n * Lease - Rental or lease agreement\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Lease extends Contract {\n override contractType = ContractType.LEASE;\n}\n\n/**\n * Agreement - Service or maintenance agreement\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Agreement extends Contract {\n override contractType = ContractType.AGREEMENT;\n}\n\n/**\n * PurchaseOrder - Order sent to vendor/supplier\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class PurchaseOrder extends Contract {\n override contractType = ContractType.PURCHASE_ORDER;\n}\n\n/**\n * WholesaleOrder - B2B order placed by a wholesale customer.\n *\n * Behaves like an Order but is conventionally created against a customer with\n * `customerType: 'wholesale'`, uses NET-30/60 payment terms, and is delivered\n * via the wholesale-portal channel rather than retail checkout.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class WholesaleOrder extends Contract {\n override contractType = ContractType.WHOLESALE_ORDER;\n}\n\n/**\n * ProductionOrder - instruction to commission goods from an internal or\n * external factory.\n *\n * The manufacturing equivalent of a {@link PurchaseOrder} — but instead of\n * buying finished inventory, you're paying to *make* it. A ProductionOrder\n * consumes raw materials per a Bill of Materials (see\n * `@happyvertical/smrt-manufacturing`) and produces finished SKU stock when\n * posted (see `@happyvertical/smrt-inventory`).\n *\n * Carries two STI-meta fields that `ProductionService.consumeMaterials` /\n * `runProduction` read from the loaded row to resolve which BOM to walk:\n *\n * - `productId` — plain string ref to the upstream `Product` (or any\n * STI subtype, e.g. apparel `Style`). Used to look up the *active*\n * BOM via `BomService.findActiveForProduct` when no explicit\n * revision was pinned at the time the order was placed.\n * - `bomId` — optional pin to a specific BOM revision. When set, the\n * manufacturing service consumes against that exact revision even\n * if a newer BOM has become active in the meantime (keeps a\n * posted run from silently switching recipes mid-flight).\n *\n * Both fields are `Meta<string>` so the scanner routes them into the\n * shared `_meta_data` JSON column on the Contract STI table instead of\n * widening the base schema with manufacturing-specific columns.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class ProductionOrder extends Contract {\n override contractType = ContractType.PRODUCTION_ORDER;\n\n /**\n * Plain string reference to the upstream product the order is asked\n * to make. Cross-package id; never `@foreignKey()`. Required by\n * `ProductionService.consumeMaterials` unless `bomId` is supplied.\n */\n productId: Meta<string> = '';\n\n /**\n * Optional explicit BOM revision the order locked to at posting\n * time. When set, manufacturing uses this exact row even if the\n * product's active BOM has since changed. When empty, falls back\n * to the active BOM for `productId`.\n */\n bomId: Meta<string> = '';\n}\n\n/**\n * Cart - transient order-in-progress for a shopper.\n *\n * Persisted as a Contract so it can hold the same line items, totals, and\n * customer reference as a real Order, then convert in place (the application\n * promotes the row from `_meta_type: Cart` to `_meta_type: Order` at checkout\n * rather than copying data between tables).\n *\n * A Cart starts in {@link ContractStatus.DRAFT} via the base `Contract.status`\n * default — the subclass doesn't need its own override. Application code\n * is responsible for promoting the row to `ContractStatus.PENDING` /\n * `ACCEPTED` at checkout time; the framework doesn't enforce a state\n * machine on the cart → order transition.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class Cart extends Contract {\n override contractType = ContractType.CART;\n}\n\n/**\n * Snapshot of the rights granted by a {@link LicenseSale}. The fields are\n * intentionally typed (not a generic JSON blob) so they survive schema\n * migrations and downstream consumers (PDF templating, hash-registry\n * publishers) can rely on the shape without re-parsing.\n *\n * Each field is opaque text — the values that make sense vary by\n * industry. A stock-media marketplace might use:\n *\n * - `medium`: `web,print,social`\n * - `distributionScope`: `worldwide` / `territorial`\n * - `exclusivity`: `exclusive` / `non-exclusive`\n * - `duration`: `perpetual` / `12-months` / `event-only`\n * - `territory`: ISO-3166 list or `worldwide`\n *\n * Booleans (`sublicensing`, `derivatives`) are typed as such; everything\n * else is a string so the model doesn't dictate vocabulary.\n */\nexport interface LicenseRightsSnapshot {\n medium: string;\n distributionScope: string;\n exclusivity: string;\n duration: string;\n territory: string;\n sublicensing: boolean;\n derivatives: boolean;\n}\n\n/**\n * Module-scoped WeakMap of \"rights snapshot the row was loaded with\".\n *\n * Used by {@link LicenseSale}'s immutability guard. We can't store the\n * captured snapshot as an instance field because the AST scanner would\n * pick it up as a persisted column; we can't store it as a `Meta<T>`\n * because then it'd round-trip with the row and the comparison would\n * always tautologically succeed. A WeakMap keyed by the instance is\n * the cleanest scope: no schema interaction, garbage-collected with\n * the instance.\n */\nconst issuedRightsSnapshot = new WeakMap<LicenseSale, string>();\n\n/**\n * LicenseSale — industry-neutral licensing primitive.\n *\n * Sits alongside the other Contract STI subtypes (Estimate, Order,\n * Lease, Agreement, PurchaseOrder, WholesaleOrder, ProductionOrder,\n * Cart) and represents a single sale of rights for a fee. A\n * stock-media marketplace selling a one-time license, a music-\n * licensing platform issuing a sync license, a code-asset marketplace\n * granting a redistribution right — all use the same primitive.\n *\n * Immutability invariant: once a `LicenseSale` is saved with\n * {@link ContractStatus.ACCEPTED}, its rights snapshot becomes\n * frozen. Re-saving with mutated rights throws. The only legal\n * transition out of ACCEPTED is `revoke()` (which moves status to\n * CANCELLED without changing the rights). If a different snapshot\n * is genuinely needed, the operator's contract is to issue a *new*\n * LicenseSale row and let the old one stand as historical record.\n *\n * All subtype-specific fields are declared as `Meta<T>` so the\n * scanner routes them into the shared `_meta_data` JSON column on\n * the `contracts` STI table — no migration of the base schema needed\n * to land this subtype.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt()\nexport class LicenseSale extends Contract {\n override contractType = ContractType.LICENSE_SALE;\n\n /**\n * Plain string reference to the upstream `Sku` (from\n * `@happyvertical/smrt-products`) that this license was sold against.\n * Cross-package id; never `@foreignKey()`.\n */\n skuId: Meta<string> = '';\n\n /**\n * Plain string reference to the `Payment` that satisfied the\n * purchase. Cross-model reference matches the rest of the\n * package's convention.\n */\n paymentId: Meta<string> = '';\n\n /**\n * The licensee's email address — the primary identity carried\n * forward into PDF generation and any downstream hash-registry\n * publication. Required; without it, downstream rights cannot be\n * attributed.\n */\n licenseeEmail: Meta<string> = '';\n\n /**\n * Optional legal-entity name of the licensee for purchases that\n * warrant it (typically high-tier B2B licenses). Empty string for\n * individual-buyer licenses where the email *is* the legal identity.\n */\n licenseeLegalEntity: Meta<string> = '';\n\n /**\n * Optional ISO-3166 jurisdiction code of the licensee, captured for\n * tax / disclosure / governing-law purposes. Empty when not\n * required.\n */\n licenseeJurisdiction: Meta<string> = '';\n\n // -------- Rights snapshot (immutable once issued) --------\n\n /** Permitted media — `web,print,social`, `broadcast`, etc. */\n rightsMedium: Meta<string> = '';\n\n /** Distribution scope — `worldwide`, `territorial`, `private`, etc. */\n rightsDistributionScope: Meta<string> = '';\n\n /** Exclusivity — `exclusive` / `non-exclusive` / `co-exclusive`. */\n rightsExclusivity: Meta<string> = '';\n\n /** Duration — `perpetual` / `12-months` / `event-only`, etc. */\n rightsDuration: Meta<string> = '';\n\n /** Territory — ISO-3166 list, `worldwide`, region name, etc. */\n rightsTerritory: Meta<string> = '';\n\n /** Whether the licensee may sublicense the rights. */\n rightsSublicensing: Meta<boolean> = false;\n\n /** Whether the licensee may create derivative works. */\n rightsDerivatives: Meta<boolean> = false;\n\n // -------- Artefacts --------\n\n /** URL of the signed PDF (typically a cloud-storage public URL). */\n pdfUrl: Meta<string> = '';\n\n /**\n * SHA-256 of the signed PDF bytes (hex string, no `0x` prefix).\n * Lets downstream consumers verify the PDF the licensee holds is\n * the one this row was issued for, without re-fetching.\n */\n pdfHash: Meta<string> = '';\n\n /**\n * Optional reference to an on-chain hash-registry entry — set when\n * the LicenseSale's `pdfHash` has been anchored on a public chain\n * for tamper-evidence. Format is whatever the hash-registry\n * service emits (e.g. `chain:tx-hash:log-index`). Empty for\n * deployments that don't publish hashes on-chain.\n */\n onChainHashRegistryRef: Meta<string> = '';\n\n constructor(options: LicenseSaleOptions = {}) {\n super(options);\n if (options.skuId !== undefined) this.skuId = options.skuId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.licenseeEmail !== undefined)\n this.licenseeEmail = options.licenseeEmail;\n if (options.licenseeLegalEntity !== undefined)\n this.licenseeLegalEntity = options.licenseeLegalEntity;\n if (options.licenseeJurisdiction !== undefined)\n this.licenseeJurisdiction = options.licenseeJurisdiction;\n if (options.rightsMedium !== undefined)\n this.rightsMedium = options.rightsMedium;\n if (options.rightsDistributionScope !== undefined)\n this.rightsDistributionScope = options.rightsDistributionScope;\n if (options.rightsExclusivity !== undefined)\n this.rightsExclusivity = options.rightsExclusivity;\n if (options.rightsDuration !== undefined)\n this.rightsDuration = options.rightsDuration;\n if (options.rightsTerritory !== undefined)\n this.rightsTerritory = options.rightsTerritory;\n if (options.rightsSublicensing !== undefined)\n this.rightsSublicensing = options.rightsSublicensing;\n if (options.rightsDerivatives !== undefined)\n this.rightsDerivatives = options.rightsDerivatives;\n if (options.pdfUrl !== undefined) this.pdfUrl = options.pdfUrl;\n if (options.pdfHash !== undefined) this.pdfHash = options.pdfHash;\n if (options.onChainHashRegistryRef !== undefined)\n this.onChainHashRegistryRef = options.onChainHashRegistryRef;\n }\n\n /**\n * Once `super.initialize()` has applied the framework's option-\n * override pass, capture the rights snapshot if (and only if) the\n * row arrived already issued. That's how the immutability guard\n * tells \"freshly drafting a license\" (snapshot still mutable) apart\n * from \"loaded an already-issued row\" (snapshot frozen).\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (this.status === ContractStatus.ACCEPTED && (await this.isSaved())) {\n issuedRightsSnapshot.set(this, this.serializeRightsSnapshot());\n }\n return this;\n }\n\n /**\n * Return the current rights snapshot as a typed object. Read-only\n * for callers — mutation should go through assignment to the\n * individual `rights*` fields, gated by the issued-immutability\n * guard.\n */\n getRightsSnapshot(): LicenseRightsSnapshot {\n return {\n medium: this.rightsMedium,\n distributionScope: this.rightsDistributionScope,\n exclusivity: this.rightsExclusivity,\n duration: this.rightsDuration,\n territory: this.rightsTerritory,\n sublicensing: this.rightsSublicensing,\n derivatives: this.rightsDerivatives,\n };\n }\n\n /**\n * Revoke an issued license — moves status to `CANCELLED` without\n * touching the rights snapshot (which stays frozen). Throws if\n * called on a non-issued license; a draft license should be\n * deleted, not revoked.\n */\n revoke(): void {\n if (this.status !== ContractStatus.ACCEPTED) {\n throw new Error(\n `LicenseSale ${this.id ?? '<new>'}: cannot revoke from status '${this.status}' — only ACCEPTED licenses can be revoked`,\n );\n }\n this.status = ContractStatus.CANCELLED;\n }\n\n /**\n * Save with the immutability check: if the row was loaded with\n * `ACCEPTED` status, the rights snapshot at save time must match\n * the snapshot at load time. Anything else means an out-of-band\n * mutation crept in and we surface it instead of letting the row\n * drift silently.\n *\n * Newly-drafted licenses (where the row was constructed without\n * `ACCEPTED` status) capture their snapshot at the moment the\n * caller transitions to `ACCEPTED` and saves — `validateImmutable`\n * picks that up post-save on the next call.\n */\n override async save(): Promise<this> {\n this.validateImmutable();\n const result = (await super.save()) as this;\n // If this save transitioned the row to ACCEPTED, freeze the\n // snapshot for future saves.\n if (\n this.status === ContractStatus.ACCEPTED &&\n !issuedRightsSnapshot.has(this)\n ) {\n issuedRightsSnapshot.set(this, this.serializeRightsSnapshot());\n }\n return result;\n }\n\n /**\n * Compare the current rights against the captured snapshot\n * (populated either at load time by `initialize` or at first\n * issuance by `save`). Throws on mismatch.\n */\n private validateImmutable(): void {\n const captured = issuedRightsSnapshot.get(this);\n if (!captured) return;\n const current = this.serializeRightsSnapshot();\n if (captured !== current) {\n throw new Error(\n `LicenseSale ${this.id ?? '<new>'}: rights snapshot is immutable once issued (status=ACCEPTED). ` +\n 'Use revoke() to cancel the existing license and issue a new one with the updated rights.',\n );\n }\n }\n\n private serializeRightsSnapshot(): string {\n // Stable key ordering so a re-serialization after a no-op\n // assignment doesn't trip the equality check.\n return JSON.stringify({\n medium: this.rightsMedium,\n distributionScope: this.rightsDistributionScope,\n exclusivity: this.rightsExclusivity,\n duration: this.rightsDuration,\n territory: this.rightsTerritory,\n sublicensing: this.rightsSublicensing,\n derivatives: this.rightsDerivatives,\n });\n }\n}\n\nexport default Contract;\n","/**\n * ContractCollection - Collection manager for Contract objects (STI)\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Contract } from '../models/Contract.js';\nimport { ContractStatus, ContractType } from '../types/index.js';\n\nexport class ContractCollection extends SmrtCollection<Contract> {\n static readonly _itemClass = Contract;\n\n /**\n * Find contracts by customer\n *\n * @param customerId - Customer ID\n * @returns Array of contracts\n */\n async findByCustomer(customerId: string): Promise<Contract[]> {\n return await this.list({\n where: { customerId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find contracts by vendor\n *\n * @param vendorId - Vendor ID\n * @returns Array of contracts\n */\n async findByVendor(vendorId: string): Promise<Contract[]> {\n return await this.list({\n where: { vendorId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find contracts by type (STI)\n *\n * @param contractType - Contract type\n * @returns Array of contracts\n */\n async findByType(contractType: ContractType): Promise<Contract[]> {\n return await this.list({\n where: { contractType },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find contracts by status\n *\n * @param status - Contract status\n * @returns Array of contracts\n */\n async findByStatus(status: ContractStatus): Promise<Contract[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all draft contracts\n *\n * @returns Array of draft contracts\n */\n async findDrafts(): Promise<Contract[]> {\n return await this.findByStatus(ContractStatus.DRAFT);\n }\n\n /**\n * Find all accepted contracts\n *\n * @returns Array of accepted contracts\n */\n async findAccepted(): Promise<Contract[]> {\n return await this.findByStatus(ContractStatus.ACCEPTED);\n }\n\n /**\n * Find overdue contracts\n *\n * @returns Array of overdue contracts\n */\n async findOverdue(): Promise<Contract[]> {\n const now = new Date().toISOString();\n // Get all contracts past due date that aren't completed or cancelled\n const all = await this.list({\n where: {\n 'dueDate <': now,\n },\n orderBy: 'dueDate ASC',\n });\n return all.filter(\n (c) =>\n c.status !== ContractStatus.COMPLETED &&\n c.status !== ContractStatus.CANCELLED,\n );\n }\n\n /**\n * Find expired estimates/quotes\n *\n * @returns Array of expired estimates\n */\n async findExpired(): Promise<Contract[]> {\n const now = new Date().toISOString();\n // Get estimates past expiry date that aren't accepted, declined, or cancelled\n const estimates = await this.list({\n where: {\n contractType: ContractType.ESTIMATE,\n 'expiryDate <': now,\n },\n orderBy: 'expiryDate ASC',\n });\n return estimates.filter(\n (c) =>\n c.status !== ContractStatus.ACCEPTED &&\n c.status !== ContractStatus.DECLINED &&\n c.status !== ContractStatus.CANCELLED,\n );\n }\n\n /**\n * Find contracts in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of contracts\n */\n async findByDateRange(startDate: Date, endDate: Date): Promise<Contract[]> {\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 // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all contracts belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of contracts for the tenant\n */\n async findByTenant(tenantId: string): Promise<Contract[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global contracts (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 contracts\n */\n async findGlobal(): Promise<Contract[]> {\n return queryGlobal<Contract>(this);\n }\n\n /**\n * Find contracts for a tenant including global contracts.\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 contracts\n */\n async findWithGlobals(tenantId: string): Promise<Contract[]> {\n return queryWithGlobals<Contract>(\n this,\n tenantId,\n 'Contract.findWithGlobals',\n );\n }\n}\n","/**\n * ContractLineItem model - individual items on a contract\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContractLineItemOptions } from '../types/index.js';\n\n/**\n * ContractLineItem represents a single line item on a contract.\n *\n * Line items contain the details of what is being sold/purchased,\n * including quantity, pricing, and optional subscription details.\n *\n * @example\n * ```typescript\n * const lineItem = await lineItems.create({\n * contractId: order.id,\n * description: 'Widget Pro',\n * quantity: 10,\n * unitPrice: 49.99,\n * taxRate: 0.08\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class ContractLineItem extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global line items\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent contract\n */\n @foreignKey('Contract')\n contractId: string = '';\n\n /**\n * Item description\n */\n description: string = '';\n\n /**\n * Quantity ordered\n */\n quantity: number = 1.0;\n\n /**\n * Unit price before discount\n */\n unitPrice: number = 0.0;\n\n /**\n * Discount amount (flat, not percentage)\n */\n discount: number = 0.0;\n\n /**\n * Tax rate as decimal (e.g., 0.08 for 8%)\n */\n taxRate: number = 0.0;\n\n /**\n * Calculated line amount (qty * price - discount + tax)\n */\n amount: number = 0.0;\n\n /**\n * Optional product reference (cross-package, plain string)\n */\n @crossPackageRef('@happyvertical/smrt-products:Product')\n productId: string = '';\n\n /**\n * SKU or item code\n */\n sku: string = '';\n\n /**\n * Start date (for lease/subscription items)\n */\n startDate: Date | null = null;\n\n /**\n * End date (for lease/subscription items)\n */\n endDate: Date | null = null;\n\n /**\n * Billing period (for subscriptions: 'monthly', 'yearly', etc.)\n */\n billingPeriod: string = '';\n\n /**\n * Sort order within the contract\n */\n sortOrder: number = 0;\n\n constructor(options: ContractLineItemOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.description !== undefined)\n this.description = options.description;\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.taxRate !== undefined) this.taxRate = options.taxRate;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.productId !== undefined) this.productId = options.productId;\n if (options.sku !== undefined) this.sku = options.sku;\n if (options.startDate !== undefined) this.startDate = options.startDate;\n if (options.endDate !== undefined) this.endDate = options.endDate;\n if (options.billingPeriod !== undefined)\n this.billingPeriod = options.billingPeriod;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n }\n\n /**\n * Calculate the line amount\n */\n calculateAmount(): number {\n const subtotal = this.quantity * this.unitPrice - this.discount;\n const tax = subtotal * this.taxRate;\n return subtotal + tax;\n }\n\n /**\n * Check if this is a subscription/recurring item\n */\n isRecurring(): boolean {\n return !!this.billingPeriod && !!this.startDate;\n }\n\n /**\n * Check if subscription is active\n */\n isSubscriptionActive(): boolean {\n if (!this.isRecurring()) return false;\n if (!this.startDate) return false;\n\n const now = new Date();\n if (now < this.startDate) return false;\n if (this.endDate && now > this.endDate) return false;\n\n return true;\n }\n}\n\nexport default ContractLineItem;\n","/**\n * ContractLineItemCollection - Collection manager for ContractLineItem objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { ContractLineItem } from '../models/ContractLineItem.js';\n\nexport class ContractLineItemCollection extends SmrtCollection<ContractLineItem> {\n static readonly _itemClass = ContractLineItem;\n\n /**\n * Find contract line items by their parent contract.\n *\n * @param contractId - Contract ID\n * @returns Array of contract line items, sorted by sortOrder\n */\n async findByContract(contractId: string): Promise<ContractLineItem[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'sortOrder ASC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all contract line items belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of contract line items for the tenant\n */\n async findByTenant(tenantId: string): Promise<ContractLineItem[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global contract 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 contract line items\n */\n async findGlobal(): Promise<ContractLineItem[]> {\n return queryGlobal<ContractLineItem>(this);\n }\n\n /**\n * Find contract 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 contract line items\n */\n async findWithGlobals(tenantId: string): Promise<ContractLineItem[]> {\n return queryWithGlobals<ContractLineItem>(\n this,\n tenantId,\n 'ContractLineItem.findWithGlobals',\n );\n }\n}\n","/**\n * CustomerCollection - Collection manager for Customer objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Customer } from '../models/Customer.js';\nimport { CustomerStatus } from '../types/index.js';\n\nexport class CustomerCollection extends SmrtCollection<Customer> {\n static readonly _itemClass = Customer;\n\n /**\n * Find customers by profile ID\n *\n * @param profileId - Profile ID from smrt-profiles\n * @returns Array of customers linked to this profile\n */\n async findByProfile(profileId: string): Promise<Customer[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active customers\n *\n * @returns Array of active customers\n */\n async findActive(): Promise<Customer[]> {\n return await this.list({\n where: { status: CustomerStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find customers by status\n *\n * @param status - Customer status\n * @returns Array of customers\n */\n async findByStatus(status: CustomerStatus): Promise<Customer[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Get or create a customer for a profile\n *\n * @param profileId - Profile ID\n * @param defaults - Default values if creating\n * @returns Customer\n */\n async getOrCreateForProfile(\n profileId: string,\n defaults: Partial<{\n creditLimit: number;\n paymentTerms: string;\n }> = {},\n ): Promise<Customer> {\n const existing = await this.findByProfile(profileId);\n if (existing.length > 0) {\n return existing[0];\n }\n\n const customer = await this.create({\n profileId,\n creditLimit: defaults.creditLimit ?? 0,\n paymentTerms: defaults.paymentTerms ?? '',\n status: CustomerStatus.ACTIVE,\n });\n await customer.save();\n return customer;\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all customers belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of customers for the tenant\n */\n async findByTenant(tenantId: string): Promise<Customer[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global customers (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 customers\n */\n async findGlobal(): Promise<Customer[]> {\n return queryGlobal<Customer>(this);\n }\n\n /**\n * Find customers for a tenant including global customers.\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 customers\n */\n async findWithGlobals(tenantId: string): Promise<Customer[]> {\n return queryWithGlobals<Customer>(\n this,\n tenantId,\n 'Customer.findWithGlobals',\n );\n }\n}\n","/**\n * Fulfillment model - tracks delivery of contract items\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type Address,\n type FulfillmentOptions,\n FulfillmentStatus,\n FulfillmentType,\n} from '../types/index.js';\n\n/**\n * Legal status transitions for a Fulfillment, keyed by the prior persisted\n * status. A status re-saved unchanged (no-op) and a brand-new row are always\n * permitted; this map governs *changes* only.\n *\n * Forward order: PENDING → PROCESSING → SHIPPED → DELIVERED, but progress may\n * SKIP intermediate states — not every order has a PROCESSING step, so\n * PENDING → SHIPPED and PENDING/PROCESSING → DELIVERED are deliberately legal.\n * The map only forbids moving BACKWARD (e.g. SHIPPED → PENDING) and leaving a\n * terminal state. CANCELLED is reachable from any non-terminal state.\n * DELIVERED and CANCELLED are terminal. This guards against raw mass-assignment\n * (the open `api:{create,update}` surface, or a stale caller) forcing, e.g., a\n * DELIVERED shipment back to PENDING or a CANCELLED one back into SHIPPED.\n * Mirrors the Contract/Payment/Payout guards added in S5 audit #1390 —\n * Fulfillment was the one money/state model missed.\n */\nconst FULFILLMENT_STATUS_TRANSITIONS: Record<\n FulfillmentStatus,\n FulfillmentStatus[]\n> = {\n [FulfillmentStatus.PENDING]: [\n FulfillmentStatus.PROCESSING,\n FulfillmentStatus.SHIPPED,\n FulfillmentStatus.DELIVERED,\n FulfillmentStatus.CANCELLED,\n ],\n [FulfillmentStatus.PROCESSING]: [\n FulfillmentStatus.SHIPPED,\n FulfillmentStatus.DELIVERED,\n FulfillmentStatus.CANCELLED,\n ],\n [FulfillmentStatus.SHIPPED]: [\n FulfillmentStatus.DELIVERED,\n FulfillmentStatus.CANCELLED,\n ],\n // Terminal states: no outbound transitions.\n [FulfillmentStatus.DELIVERED]: [],\n [FulfillmentStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each Fulfillment instance was loaded with,\n * for the save-time transition guard. A WeakMap keeps it out of the schema and\n * GCs with the instance — same pattern as the other commerce models.\n */\nconst loadedFulfillmentStatus = new WeakMap<Fulfillment, FulfillmentStatus>();\n\n/**\n * Fulfillment tracks the delivery or completion of contract items.\n *\n * A contract can have multiple fulfillments (e.g., partial shipments).\n * Each fulfillment tracks what was delivered and when.\n *\n * @example\n * ```typescript\n * const fulfillment = await fulfillments.create({\n * contractId: order.id,\n * fulfillmentType: FulfillmentType.SHIPMENT,\n * trackingNumber: 'UPS-123456789',\n * carrier: 'UPS',\n * shippingAddress: {\n * street1: '123 Main St',\n * city: 'Anytown',\n * state: 'CA',\n * postalCode: '90210',\n * country: 'US'\n * }\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Fulfillment extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global fulfillments\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent contract being fulfilled\n */\n @foreignKey('Contract')\n contractId: string = '';\n\n /**\n * Type of fulfillment\n */\n fulfillmentType: FulfillmentType = FulfillmentType.SHIPMENT;\n\n /**\n * Current fulfillment status\n */\n status: FulfillmentStatus = FulfillmentStatus.PENDING;\n\n /**\n * Tracking number from carrier\n */\n trackingNumber: string = '';\n\n /**\n * Shipping carrier name\n */\n carrier: string = '';\n\n /**\n * Shipping destination address\n */\n shippingAddress: Address = {};\n\n /**\n * When the shipment was sent\n */\n shippedAt: Date | null = null;\n\n /**\n * When the shipment was delivered\n */\n deliveredAt: Date | null = null;\n\n /**\n * Estimated delivery date\n */\n estimatedDelivery: Date | null = null;\n\n /**\n * Notes about this fulfillment\n */\n notes: string = '';\n\n constructor(options: FulfillmentOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.fulfillmentType !== undefined)\n this.fulfillmentType = options.fulfillmentType;\n if (options.status !== undefined) this.status = options.status;\n if (options.trackingNumber !== undefined)\n this.trackingNumber = options.trackingNumber;\n if (options.carrier !== undefined) this.carrier = options.carrier;\n if (options.shippingAddress !== undefined)\n this.shippingAddress = options.shippingAddress;\n if (options.shippedAt !== undefined) this.shippedAt = options.shippedAt;\n if (options.deliveredAt !== undefined)\n this.deliveredAt = options.deliveredAt;\n if (options.estimatedDelivery !== undefined)\n this.estimatedDelivery = options.estimatedDelivery;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Capture the status the row was loaded with so the save-time transition\n * guard can reject illegal status flips made via raw field assignment\n * (mass-assignment on the generated update route, a stale caller, etc.).\n * Only persisted rows carry a prior status.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedFulfillmentStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Validate the status transition before persisting, then save. Blocks a\n * forged `status` written via raw mass-assignment on the open\n * `api:{create,update}` surface. See S5 audit #1390 follow-up.\n */\n override async save(): Promise<this> {\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n const result = (await super.save()) as this;\n loadedFulfillmentStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Reject an illegal status flip done via raw assignment. No-op transitions\n * and brand-new rows are always allowed.\n */\n private assertStatusTransition(prior: FulfillmentStatus | 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 = FULFILLMENT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Fulfillment ${this.trackingNumber || this.id}: illegal status ` +\n `transition '${prior}' → '${this.status}'. Use the guarded helpers ` +\n '(markShipped / markDelivered / cancel).',\n );\n }\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status. The WeakMap is only populated when\n * {@link initialize} loaded the row; it is empty for an instance built via\n * `collection.create({ id: <existing>, _skipLoad: true })` (the upsert path\n * that writes onto an existing row without hydrating it). Trusting an empty\n * WeakMap there would treat the write as a brand-new row and skip the guard.\n * So when this instance carries an `id`, read the persisted row straight from\n * the DB and treat its `status` as the prior. `undefined` = genuinely new.\n * Mirrors the authoritative-prior-load on Contract/Payment/Invoice/Payout.\n */\n private async resolvePriorStatus(): Promise<FulfillmentStatus | undefined> {\n if (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 FulfillmentStatus;\n }\n } catch {\n // DB not ready / table absent — fall through to the in-memory record.\n }\n }\n return loadedFulfillmentStatus.get(this);\n }\n\n /**\n * Check if fulfillment is complete\n */\n isDelivered(): boolean {\n return this.status === FulfillmentStatus.DELIVERED;\n }\n\n /**\n * Check if fulfillment is in transit\n */\n isInTransit(): boolean {\n return this.status === FulfillmentStatus.SHIPPED;\n }\n\n /**\n * Check if fulfillment is pending\n */\n isPending(): boolean {\n return this.status === FulfillmentStatus.PENDING;\n }\n\n /**\n * Assert the current in-memory status may legally transition to `next`,\n * surfacing the illegal-transition error at the mutation call site rather\n * than deferring it to save(). A no-op (already in `next`) is allowed.\n */\n private assertCanTransitionTo(next: FulfillmentStatus): void {\n if (this.status === next) return;\n const allowed = FULFILLMENT_STATUS_TRANSITIONS[this.status] ?? [];\n if (!allowed.includes(next)) {\n throw new Error(\n `Fulfillment ${this.trackingNumber || this.id || '<new>'}: cannot move ` +\n `from '${this.status}' to '${next}'.`,\n );\n }\n }\n\n /**\n * Mark as shipped. Rejected from a terminal state (DELIVERED / CANCELLED).\n */\n markShipped(trackingNumber?: string, carrier?: string): void {\n this.assertCanTransitionTo(FulfillmentStatus.SHIPPED);\n this.status = FulfillmentStatus.SHIPPED;\n this.shippedAt = new Date();\n if (trackingNumber) this.trackingNumber = trackingNumber;\n if (carrier) this.carrier = carrier;\n }\n\n /**\n * Mark as delivered. Rejected from CANCELLED (a cancelled shipment can't be\n * delivered).\n */\n markDelivered(): void {\n this.assertCanTransitionTo(FulfillmentStatus.DELIVERED);\n this.status = FulfillmentStatus.DELIVERED;\n this.deliveredAt = new Date();\n }\n\n /**\n * Cancel fulfillment. Rejected once DELIVERED (a delivered shipment can't be\n * cancelled — model a return/refund elsewhere instead).\n */\n cancel(): void {\n this.assertCanTransitionTo(FulfillmentStatus.CANCELLED);\n this.status = FulfillmentStatus.CANCELLED;\n }\n}\n\nexport default Fulfillment;\n","/**\n * FulfillmentCollection - Collection manager for Fulfillment objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Fulfillment } from '../models/Fulfillment.js';\nimport { FulfillmentStatus, type FulfillmentType } from '../types/index.js';\n\nexport class FulfillmentCollection extends SmrtCollection<Fulfillment> {\n static readonly _itemClass = Fulfillment;\n\n /**\n * Find fulfillments by contract\n *\n * @param contractId - Contract ID\n * @returns Array of fulfillments\n */\n async findByContract(contractId: string): Promise<Fulfillment[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find fulfillments by status\n *\n * @param status - Fulfillment status\n * @returns Array of fulfillments\n */\n async findByStatus(status: FulfillmentStatus): Promise<Fulfillment[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find fulfillments by type\n *\n * @param fulfillmentType - Fulfillment type\n * @returns Array of fulfillments\n */\n async findByType(fulfillmentType: FulfillmentType): Promise<Fulfillment[]> {\n return await this.list({\n where: { fulfillmentType },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all pending fulfillments\n *\n * @returns Array of pending fulfillments\n */\n async findPending(): Promise<Fulfillment[]> {\n return await this.findByStatus(FulfillmentStatus.PENDING);\n }\n\n /**\n * Find all in-transit shipments\n *\n * @returns Array of shipped fulfillments\n */\n async findInTransit(): Promise<Fulfillment[]> {\n return await this.findByStatus(FulfillmentStatus.SHIPPED);\n }\n\n /**\n * Find by tracking number\n *\n * @param trackingNumber - Tracking number\n * @returns Fulfillment or null\n */\n async findByTracking(trackingNumber: string): Promise<Fulfillment | null> {\n const results = await this.list({\n where: { trackingNumber },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Find fulfillments shipped in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of fulfillments\n */\n async findShippedInRange(\n startDate: Date,\n endDate: Date,\n ): Promise<Fulfillment[]> {\n return await this.list({\n where: {\n 'shippedAt >=': startDate.toISOString(),\n 'shippedAt <=': endDate.toISOString(),\n },\n orderBy: 'shippedAt DESC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all fulfillments belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of fulfillments for the tenant\n */\n async findByTenant(tenantId: string): Promise<Fulfillment[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global fulfillments (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 fulfillments\n */\n async findGlobal(): Promise<Fulfillment[]> {\n return queryGlobal<Fulfillment>(this);\n }\n\n /**\n * Find fulfillments for a tenant including global fulfillments.\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 fulfillments\n */\n async findWithGlobals(tenantId: string): Promise<Fulfillment[]> {\n return queryWithGlobals<Fulfillment>(\n this,\n tenantId,\n 'Fulfillment.findWithGlobals',\n );\n }\n}\n","/**\n * FulfillmentLineItem model - tracks which contract items were fulfilled\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { FulfillmentLineItemOptions } from '../types/index.js';\n\n/**\n * Sub-unit rounding tolerance for the over-fulfillment cap, matching the rest\n * of the package's EPSILON convention. Quantities are decimal, so a tiny\n * float-summation overshoot must not trip the guard.\n */\nconst FULFILLMENT_QUANTITY_EPSILON = 0.01;\n\n/**\n * FulfillmentLineItem tracks which contract line items are included\n * in a specific fulfillment and how much was fulfilled.\n *\n * This allows partial fulfillment - e.g., shipping 5 of 10 items ordered.\n *\n * @example\n * ```typescript\n * const fulfillmentItem = await fulfillmentItems.create({\n * fulfillmentId: fulfillment.id,\n * contractLineItemId: lineItem.id,\n * quantityFulfilled: 5 // of 10 ordered\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class FulfillmentLineItem extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global fulfillment line items\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent fulfillment\n */\n @foreignKey('Fulfillment')\n fulfillmentId: string = '';\n\n /**\n * Contract line item being fulfilled\n */\n @foreignKey('ContractLineItem')\n contractLineItemId: string = '';\n\n /**\n * Quantity fulfilled in this fulfillment\n */\n quantityFulfilled: number = 1.0;\n\n /**\n * Notes about this line item\n */\n notes: string = '';\n\n constructor(options: FulfillmentLineItemOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.fulfillmentId !== undefined)\n this.fulfillmentId = options.fulfillmentId;\n if (options.contractLineItemId !== undefined)\n this.contractLineItemId = options.contractLineItemId;\n if (options.quantityFulfilled !== undefined)\n this.quantityFulfilled = options.quantityFulfilled;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Save-time over-fulfillment guard (S5 audit #1390 follow-up, round 2):\n * - `quantityFulfilled` must be a finite, positive number,\n * - the referenced `ContractLineItem` must resolve **within the caller's\n * tenant scope** AND belong to the same contract as the parent\n * Fulfillment, and\n * - the sum of all FulfillmentLineItems against that ContractLineItem\n * (this row included) must not exceed the ordered `ContractLineItem.quantity`.\n *\n * Without this, a caller could ship 50 of 10 ordered — the direct parallel\n * to the PaymentAllocation over-allocation hole #1390 closed.\n *\n * Tenant isolation (round 2): the ContractLineItem is loaded through\n * `ContractLineItemCollection`, so it goes through the tenancy\n * auto-filtering interceptors. A cross-tenant `contractLineItemId` therefore\n * fails closed — it resolves to `null` and trips the existing \"does not\n * exist\" error rather than leaking a foreign-tenant ordered quantity. The\n * raw `this.db.get('contract_line_items', …)` it replaced bypassed those\n * filters. We additionally load the parent Fulfillment (also tenant-scoped)\n * and reject when the line item belongs to a *different* contract than the\n * Fulfillment is fulfilling, so a valid-but-unrelated line id can't be\n * used to fulfill against the wrong order.\n */\n override async save(): Promise<this> {\n if (\n !Number.isFinite(this.quantityFulfilled) ||\n this.quantityFulfilled <= 0\n ) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: quantityFulfilled must be a ` +\n `positive number (got ${this.quantityFulfilled}).`,\n );\n }\n\n if (this.contractLineItemId) {\n const { ContractLineItemCollection } = await import(\n '../collections/ContractLineItemCollection.js'\n );\n const lineItems = await ContractLineItemCollection.create(this.options);\n const orderedLineItem = await lineItems.get(this.contractLineItemId);\n if (!orderedLineItem) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: referenced ContractLineItem ` +\n `'${this.contractLineItemId}' does not exist — refusing to fulfill ` +\n 'against a missing line item (the over-fulfillment cap cannot be ' +\n 'enforced otherwise).',\n );\n }\n\n // The line item must belong to the same contract the parent Fulfillment\n // is fulfilling. Otherwise a caller could point a Fulfillment for\n // contract A at a line item from contract B (over-fulfilling B's order\n // via A's shipment, and falsifying both contracts' fulfilled totals).\n if (this.fulfillmentId) {\n const { FulfillmentCollection } = await import(\n '../collections/FulfillmentCollection.js'\n );\n const fulfillments = await FulfillmentCollection.create(this.options);\n const fulfillment = await fulfillments.get(this.fulfillmentId);\n if (\n fulfillment &&\n orderedLineItem.contractId !== fulfillment.contractId\n ) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: ContractLineItem ` +\n `'${this.contractLineItemId}' belongs to contract ` +\n `'${orderedLineItem.contractId}', but its Fulfillment ` +\n `'${this.fulfillmentId}' fulfills contract ` +\n `'${fulfillment.contractId}' — refusing to fulfill a line item ` +\n 'from a different contract.',\n );\n }\n }\n\n const ordered = Number(orderedLineItem.quantity);\n\n const { FulfillmentLineItemCollection } = await import(\n '../collections/FulfillmentLineItemCollection.js'\n );\n const siblings = await FulfillmentLineItemCollection.create(this.options);\n const existing = await siblings.findByContractLineItem(\n this.contractLineItemId,\n );\n const otherFulfilled = existing.reduce(\n (sum: number, item: FulfillmentLineItem) =>\n item.id === this.id ? sum : sum + item.quantityFulfilled,\n 0,\n );\n\n if (\n Number.isFinite(ordered) &&\n otherFulfilled + this.quantityFulfilled - ordered >\n FULFILLMENT_QUANTITY_EPSILON\n ) {\n throw new Error(\n `FulfillmentLineItem ${this.id ?? '<new>'}: fulfilling ` +\n `${this.quantityFulfilled} would over-fulfill contract line ` +\n `'${this.contractLineItemId}' — already fulfilled ${otherFulfilled} ` +\n `of ${ordered} ordered.`,\n );\n }\n }\n\n return super.save() as Promise<this>;\n }\n}\n\nexport default FulfillmentLineItem;\n","/**\n * FulfillmentLineItemCollection - Collection manager for FulfillmentLineItem objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { FulfillmentLineItem } from '../models/FulfillmentLineItem.js';\n\nexport class FulfillmentLineItemCollection extends SmrtCollection<FulfillmentLineItem> {\n static readonly _itemClass = FulfillmentLineItem;\n\n /**\n * Find fulfillment line items by their parent fulfillment.\n *\n * @param fulfillmentId - Fulfillment ID\n * @returns Array of fulfillment line items\n */\n async findByFulfillment(\n fulfillmentId: string,\n ): Promise<FulfillmentLineItem[]> {\n return await this.list({\n where: { fulfillmentId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find every fulfillment line item that fulfills a given contract line item,\n * across all fulfillments. Used by the over-fulfillment cap to sum how much\n * of an ordered line has already been fulfilled.\n *\n * @param contractLineItemId - Contract line item ID\n * @returns Array of fulfillment line items targeting that contract line\n */\n async findByContractLineItem(\n contractLineItemId: string,\n ): Promise<FulfillmentLineItem[]> {\n return await this.list({\n where: { contractLineItemId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Sum the quantity already fulfilled for a contract line item across all\n * fulfillments.\n *\n * @param contractLineItemId - Contract line item ID\n * @returns Total quantity fulfilled\n */\n async getTotalFulfilledForContractLine(\n contractLineItemId: string,\n ): Promise<number> {\n const items = await this.findByContractLineItem(contractLineItemId);\n return items.reduce((sum, item) => sum + item.quantityFulfilled, 0);\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all fulfillment line items belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of fulfillment line items for the tenant\n */\n async findByTenant(tenantId: string): Promise<FulfillmentLineItem[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global fulfillment 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 fulfillment line items\n */\n async findGlobal(): Promise<FulfillmentLineItem[]> {\n return queryGlobal<FulfillmentLineItem>(this);\n }\n\n /**\n * Find fulfillment 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 fulfillment line items\n */\n async findWithGlobals(tenantId: string): Promise<FulfillmentLineItem[]> {\n return queryWithGlobals<FulfillmentLineItem>(\n this,\n tenantId,\n 'FulfillmentLineItem.findWithGlobals',\n );\n }\n}\n","/**\n * Invoice model - billing document sent to customers\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Journal, JournalEntryData } from '@happyvertical/smrt-ledgers';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type AccountingInvoiceInput,\n type InvoiceOptions,\n InvoiceStatus,\n type RecognizeRevenueOptions,\n} from '../types/index.js';\nimport type { InvoiceLineItem } from './InvoiceLineItem.js';\n\n/**\n * Sub-cent rounding tolerance for the invoice's own integrity checks (forged\n * total / over-payment / payment-status consistency). Used when comparing\n * caller-supplied totals against recomputed line-item totals so floating-point\n * fuzz doesn't trip the guards.\n *\n * NOTE: this is intentionally looser than the smrt-ledgers `BALANCE_EPSILON`\n * (0.001). To keep a no-line-item invoice ledger-postable, {@link\n * Invoice.assertTotalArithmetic} SNAPS `totalAmount` to exactly\n * `subtotal + taxAmount` after the tolerance check, so every journal built in\n * {@link Invoice.recognizeRevenue} balances under the tighter ledger epsilon.\n */\nconst INVOICE_EPSILON = 0.01;\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 (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.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.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.PARTIAL,\n InvoiceStatus.VIEWED,\n InvoiceStatus.SENT,\n InvoiceStatus.OVERDUE,\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 '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: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Invoice extends SmrtObject {\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 @foreignKey('Customer')\n customerId: string = '';\n\n /**\n * Optional link to Contract (cross-package reference)\n */\n @foreignKey('Contract')\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 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 issueDate: Date = new Date();\n\n /**\n * Payment due date\n */\n dueDate: Date = new Date();\n\n /**\n * Date invoice was fully paid\n */\n paidDate: Date | null = null;\n\n // ============================================================================\n // Amounts\n // ============================================================================\n\n /**\n * Subtotal before tax\n */\n subtotal: number = 0;\n\n /**\n * Tax amount\n */\n taxAmount: number = 0;\n\n /**\n * Total amount due (subtotal + tax)\n */\n totalAmount: number = 0;\n\n /**\n * Amount paid (sum of PaymentAllocations)\n */\n amountPaid: number = 0;\n\n /**\n * Currency code (ISO 4217)\n */\n currency: string = 'CAD';\n\n // ============================================================================\n // Status\n // ============================================================================\n\n /**\n * Current invoice status\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 /**\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 // ============================================================================\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 notes: string = '';\n\n /**\n * Notes shown to customer on invoice\n */\n customerNotes: string = '';\n\n /**\n * Payment terms text\n */\n terms: string = '';\n\n constructor(options: InvoiceOptions = {}) {\n super(options);\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.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.status !== undefined) this.status = options.status;\n if (options.arJournalId !== undefined)\n this.arJournalId = options.arJournalId;\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.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` (beyond rounding tolerance).\n * - **Non-negativity** is enforced on all four amounts.\n * - **Status transitions** are validated against the prior persisted status.\n */\n override async save(): Promise<this> {\n const persisted = await this.isSaved();\n const prior = await this.resolvePriorStatus(persisted);\n\n await this.recomputeAmountsForSave(persisted);\n this.assertNonNegativeAmounts();\n this.assertStatusTransition(prior);\n this.assertPaymentStatusConsistent();\n\n const result = (await super.save()) as this;\n // Once written, the current status becomes the new \"prior\" for the next\n // save in this instance's lifetime.\n loadedInvoiceStatus.set(this, this.status);\n return result;\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 );\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 = lineItems.reduce(\n (sum: number, item: InvoiceLineItem) => sum + item.getTaxAmount(),\n 0,\n );\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 const { PaymentAllocationCollection } = await import(\n '../collections/PaymentAllocationCollection.js'\n );\n const allocationCollection = await PaymentAllocationCollection.create(\n this.options,\n );\n const allocated = await allocationCollection.getTotalAllocatedToInvoice(\n this.id,\n );\n this.amountPaid = allocated;\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 /**\n * Whether `amountPaid` covers `totalAmount` within the sub-cent rounding\n * tolerance. This is the SINGLE source of truth for the **PAID** decision —\n * both {@link updatePaymentStatus} (which decides PAID) and\n * {@link assertPaymentStatusConsistent} (which validates PAID on save) call\n * it, so the PAID-deciding comparison and the PAID-validating comparison can\n * never drift apart. A strict `amountPaid >= totalAmount` here would diverge\n * from the epsilon-tolerant guard: a float-summed total paid exactly (e.g.\n * `0.1 × 3` line items paid `0.3`) reads as \"not covered\" by `>=` but\n * \"covered\" by the guard, so `updatePaymentStatus` would set PARTIAL and the\n * save-time guard would then reject it — leaving a genuinely-paid invoice\n * unsaveable (S5 audit #1390 follow-up).\n *\n * NOTE: the claim is scoped to PAID. The PARTIAL branch is NOT unified the\n * same way — {@link updatePaymentStatus} treats any `amountPaid > 0` as\n * PARTIAL, whereas {@link isPartiallyPaid} (used by the save-time guard)\n * requires `amountPaid > INVOICE_EPSILON`. That pre-existing asymmetry only\n * matters for a sub-cent dust payment and is intentionally left as-is.\n */\n private isFullyPaid(): boolean {\n return this.amountPaid - this.totalAmount >= -INVOICE_EPSILON;\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 > INVOICE_EPSILON && !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 open states SENT / VIEWED). Lifecycle statuses that aren't a\n * function of amountPaid — DRAFT, 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.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 > INVOICE_EPSILON &&\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 /**\n * Enforce `totalAmount === subtotal + taxAmount` (within rounding tolerance),\n * then SNAP `totalAmount` to the exact arithmetic. Used when the invoice has\n * no line items to recompute from.\n *\n * The snap closes the invoice-vs-ledger epsilon gap (S5 audit #1390\n * follow-up): the invoice guard tolerates `INVOICE_EPSILON` (0.01) but the\n * smrt-ledgers balance check uses a tighter `BALANCE_EPSILON` (0.001). A\n * no-line-item invoice with, say, subtotal 100.00 / total 100.005 passes this\n * guard, yet `recognizeRevenue` would build DR 100.005 / CR 100.00 and\n * `journal.post()` would reject it as unbalanced — voiding the journal and\n * permanently blocking revenue recognition. Snapping `totalAmount` to\n * `subtotal + taxAmount` here (after the tolerance check) means the persisted\n * total and every journal built from it are always ledger-consistent.\n */\n private assertTotalArithmetic(): void {\n const expected = this.subtotal + this.taxAmount;\n if (Math.abs(this.totalAmount - expected) > INVOICE_EPSILON) {\n throw new Error(\n `Invoice ${this.invoiceNumber || this.id || '<new>'}: totalAmount ${this.totalAmount} ` +\n `must equal subtotal + taxAmount (${expected}).`,\n );\n }\n this.totalAmount = expected;\n }\n\n /**\n * Reject negative financial values and an amountPaid that exceeds the total\n * (beyond rounding tolerance — overpayment is modelled elsewhere, not by\n * letting amountPaid float above the invoice total).\n */\n private assertNonNegativeAmounts(): void {\n const label = this.invoiceNumber || this.id || '<new>';\n for (const [field, value] of [\n ['subtotal', this.subtotal],\n ['taxAmount', this.taxAmount],\n ['totalAmount', this.totalAmount],\n ['amountPaid', this.amountPaid],\n ] as const) {\n if (!Number.isFinite(value) || 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 > INVOICE_EPSILON) {\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.totalAmount - this.amountPaid);\n }\n\n // ============================================================================\n // Status Transitions\n // ============================================================================\n\n /**\n * Mark invoice as sent\n */\n markSent(): void {\n if (this.status !== InvoiceStatus.DRAFT) {\n throw new Error(\n `Cannot send invoice with status '${this.status}'. Only draft 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 * (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 this.status = InvoiceStatus.PAID;\n this.paidDate = new Date();\n } else if (amountPaid > 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.sentAt) {\n this.status = this.viewedAt ? InvoiceStatus.VIEWED : InvoiceStatus.SENT;\n } else {\n this.status = InvoiceStatus.DRAFT;\n }\n }\n }\n\n /**\n * Cancel the invoice\n */\n cancel(): void {\n if (this.isPaid()) {\n throw new Error(\n `Cannot cancel invoice with status '${this.status}'. Paid invoices cannot be cancelled.`,\n );\n }\n this.status = InvoiceStatus.CANCELLED;\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 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 const persisted = await this.isSaved();\n await this.recomputeAmountsForSave(persisted);\n this.assertNonNegativeAmounts();\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.\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(): Promise<AccountingInvoiceInput> {\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(),\n ),\n subtotal: this.subtotal,\n taxAmount: this.taxAmount,\n totalAmount: this.totalAmount,\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 { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Invoice } from '../models/Invoice.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 /**\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","/**\n * InvoiceLineItem model - individual items on an invoice\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n AccountingLineItemInput,\n InvoiceLineItemOptions,\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: 0.01, // per impression\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: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class InvoiceLineItem extends SmrtObject {\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 @foreignKey('Invoice')\n invoiceId: string = '';\n\n /**\n * Item description\n */\n description: string = '';\n\n /**\n * SKU or item code\n */\n sku: string = '';\n\n /**\n * Quantity (e.g., impressions, hours, units)\n */\n quantity: number = 1;\n\n /**\n * Unit price before discount\n */\n unitPrice: number = 0;\n\n /**\n * Discount amount (flat, not percentage)\n */\n discount: number = 0;\n\n /**\n * Tax rate as decimal (e.g., 0.05 for 5%)\n */\n taxRate: number = 0;\n\n /**\n * Calculated line amount\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')\n revenueAccountId: string = '';\n\n /**\n * Sort order within the invoice\n */\n sortOrder: number = 0;\n\n constructor(options: InvoiceLineItemOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.invoiceId !== undefined) this.invoiceId = options.invoiceId;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.sku !== undefined) this.sku = options.sku;\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.taxRate !== undefined) this.taxRate = options.taxRate;\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 /**\n * Calculate the line amount.\n *\n * Formula: (quantity * unitPrice - discount) * (1 + taxRate)\n *\n * Tax is calculated on the discounted subtotal. This follows the common\n * \"discount before tax\" approach used in most North American jurisdictions.\n * For jurisdictions requiring different tax calculation methods, override\n * this method or calculate amounts externally.\n */\n calculateAmount(): number {\n const subtotal = this.quantity * this.unitPrice - this.discount;\n const tax = subtotal * this.taxRate;\n return subtotal + tax;\n }\n\n /**\n * Get subtotal (before tax)\n */\n getSubtotal(): number {\n return this.quantity * this.unitPrice - this.discount;\n }\n\n /**\n * Get tax amount\n */\n getTaxAmount(): number {\n return this.getSubtotal() * this.taxRate;\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 toAccountingLineItem(): AccountingLineItemInput {\n return {\n description: this.description,\n sku: this.sku || undefined,\n quantity: this.quantity,\n unitPrice: this.unitPrice,\n discount: this.discount || undefined,\n taxRate: this.taxRate || undefined,\n amount: 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 { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { InvoiceLineItem } from '../models/InvoiceLineItem.js';\nimport type { AccountingLineItemInput } from '../types/index.js';\n\nexport class InvoiceLineItemCollection extends SmrtCollection<InvoiceLineItem> {\n static readonly _itemClass = InvoiceLineItem;\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\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","/**\n * PaymentAllocation model - maps payments to invoices\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { PaymentAllocationOptions } from '../types/index.js';\n\n/**\n * Sub-cent rounding tolerance for over-allocation checks, matching the rest\n * of the package's EPSILON convention.\n */\nconst ALLOCATION_EPSILON = 0.01;\n\n/**\n * PaymentAllocation tracks how payments are applied to invoices.\n *\n * This enables:\n * - Partial payments (one payment partially covering an invoice)\n * - Split payments (one payment split across multiple invoices)\n * - Payment history per invoice\n *\n * **Note**: This model does not automatically validate that allocations\n * don't exceed the payment amount or invoice amount due. Use\n * `PaymentAllocationCollection.getUnallocatedFromPayment()` before creating\n * allocations to ensure sufficient funds are available.\n *\n * @example\n * ```typescript\n * import { PaymentAllocationCollection } from '@happyvertical/smrt-commerce';\n *\n * // Get the collection\n * const allocations = await PaymentAllocationCollection.create(options);\n *\n * // Check available funds before allocating\n * const available = await allocations.getUnallocatedFromPayment(\n * payment.id,\n * payment.amount\n * );\n *\n * if (available >= amountToAllocate) {\n * const allocation = await allocations.create({\n * paymentId: payment.id,\n * invoiceId: invoice.id,\n * amount: amountToAllocate,\n * allocatedBy: 'user-uuid'\n * });\n *\n * // Update invoice payment status\n * const totalAllocated = await allocations.getTotalAllocatedToInvoice(invoice.id);\n * invoice.updatePaymentStatus(totalAllocated);\n * await invoice.save();\n * }\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // NOTE: `create` and `delete` are intentionally NOT exposed over the\n // generated REST/MCP surface (S5 audit #1390). Allocation rows directly\n // determine an invoice's amountPaid/status and a payment's remaining funds;\n // a generated `create` lets a caller forge an allocation that over-applies a\n // payment, and a generated `delete` lets a caller silently un-apply funds —\n // both falsify balances with no authz. Allocations must be created/removed\n // through application code that re-derives invoice status and respects the\n // payment-amount cap (the cap is also enforced in `save()` as defence in\n // depth).\n //\n // CLI parity (round 6, codex ROOT INSIGHT): the CLI is an independently\n // configured write surface. `cli: true` would still generate create/update/\n // delete commands that forge or un-apply allocations, re-opening the exact\n // vector closed on api/mcp. The CLI is locked to the same read-only surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class PaymentAllocation extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global payment allocations\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Payment being allocated\n */\n @foreignKey('Payment')\n paymentId: string = '';\n\n /**\n * Invoice receiving the allocation\n */\n @foreignKey('Invoice')\n invoiceId: string = '';\n\n /**\n * Amount allocated from payment to invoice\n */\n amount: number = 0;\n\n /**\n * When the allocation was made\n */\n allocatedAt: Date = new Date();\n\n /**\n * User/agent who made the allocation\n */\n allocatedBy: string = '';\n\n /**\n * Notes about the allocation\n */\n notes: string = '';\n\n constructor(options: PaymentAllocationOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.invoiceId !== undefined) this.invoiceId = options.invoiceId;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.allocatedAt !== undefined)\n this.allocatedAt = options.allocatedAt;\n if (options.allocatedBy !== undefined)\n this.allocatedBy = options.allocatedBy;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Save-time integrity guard (S5 audit #1390 + follow-up):\n * - allocation `amount` must be a finite, positive number,\n * - the sum of all allocations against the referenced Payment (this row\n * included) must not exceed the Payment's amount — over-applying a\n * payment across invoices would falsify both payment and invoice\n * balances, and\n * - the sum of all allocations against the referenced Invoice (this row\n * included) must not exceed the Invoice's `totalAmount`.\n *\n * The Payment-amount cap is enforced against the persisted Payment row. An\n * allocation always carries a `@foreignKey('Payment')` paymentId, so a\n * `paymentId` that doesn't resolve to a real Payment is a **hard error** (S5\n * audit #1390 round 2): previously a missing Payment silently skipped the cap\n * entirely, letting a caller over-apply (or fabricate) funds simply by\n * pointing at a non-existent payment. An empty `paymentId` is still rejected\n * by the underlying FK requirement; the positivity check always applies.\n *\n * The Invoice-total cap (follow-up) closes a complementary hole: the\n * per-Payment cap lets allocations from *different* payments each pass their\n * own check while jointly summing above the invoice total. The next\n * `Invoice.save()` would then recompute `amountPaid` from these allocations,\n * trip `assertNonNegativeAmounts` (amountPaid > totalAmount), and leave the\n * invoice permanently unsaveable while the over-allocations persist. Capping\n * here keeps allocations from ever exceeding what the invoice owes. The cap\n * is skipped only when the invoice row can't be resolved (e.g. ledger-less /\n * not-yet-persisted) so it never blocks an otherwise-valid allocation.\n */\n override async save(): Promise<this> {\n if (!Number.isFinite(this.amount) || this.amount <= 0) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: amount must be a positive number (got ${this.amount}).`,\n );\n }\n\n if (this.paymentId) {\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await PaymentCollection.create(this.options);\n const payment = await payments.get({ id: this.paymentId });\n if (!payment) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: referenced Payment ` +\n `'${this.paymentId}' does not exist — refusing to allocate against a ` +\n 'missing payment (the payment-amount cap cannot be enforced otherwise).',\n );\n }\n const { PaymentAllocationCollection } = await import(\n '../collections/PaymentAllocationCollection.js'\n );\n const allocations = await PaymentAllocationCollection.create(\n this.options,\n );\n const existing = await allocations.findByPayment(this.paymentId);\n const otherTotal = existing.reduce(\n (sum: number, alloc: PaymentAllocation) =>\n alloc.id === this.id ? sum : sum + alloc.amount,\n 0,\n );\n if (otherTotal + this.amount - payment.amount > ALLOCATION_EPSILON) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: allocating ${this.amount} would over-apply ` +\n `payment '${this.paymentId}' — already allocated ${otherTotal} of ${payment.amount}.`,\n );\n }\n }\n\n if (this.invoiceId) {\n const { InvoiceCollection } = await import(\n '../collections/InvoiceCollection.js'\n );\n const invoices = await InvoiceCollection.create(this.options);\n const invoice = await invoices.get({ id: this.invoiceId });\n // Only enforce when the invoice resolves and carries a real positive\n // total — a missing/zero-total invoice (not yet persisted, ledger-less\n // test fixture) skips the cap rather than blocking a valid allocation.\n // `Number.isFinite(0)` is `true`, so the prior `isFinite` guard wrongly\n // capped a freshly-created total=0 invoice at 0 and rejected every\n // allocation against it; gate on `> ALLOCATION_EPSILON` to match the\n // documented \"zero/missing total skips the cap\" intent.\n if (invoice && invoice.totalAmount > ALLOCATION_EPSILON) {\n const { PaymentAllocationCollection } = await import(\n '../collections/PaymentAllocationCollection.js'\n );\n const allocations = await PaymentAllocationCollection.create(\n this.options,\n );\n const existingForInvoice = await allocations.findByInvoice(\n this.invoiceId,\n );\n const otherInvoiceTotal = existingForInvoice.reduce(\n (sum: number, alloc: PaymentAllocation) =>\n alloc.id === this.id ? sum : sum + alloc.amount,\n 0,\n );\n if (\n otherInvoiceTotal + this.amount - invoice.totalAmount >\n ALLOCATION_EPSILON\n ) {\n throw new Error(\n `PaymentAllocation ${this.id ?? '<new>'}: allocating ${this.amount} would over-pay ` +\n `invoice '${this.invoiceId}' — already allocated ${otherInvoiceTotal} of ` +\n `${invoice.totalAmount}.`,\n );\n }\n }\n }\n\n return super.save() as Promise<this>;\n }\n}\n\nexport default PaymentAllocation;\n","/**\n * PaymentAllocationCollection - Collection manager for PaymentAllocation objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { PaymentAllocation } from '../models/PaymentAllocation.js';\n\nexport class PaymentAllocationCollection extends SmrtCollection<PaymentAllocation> {\n static readonly _itemClass = PaymentAllocation;\n\n /**\n * Find allocations by payment\n *\n * @param paymentId - Payment ID\n * @returns Array of allocations\n */\n async findByPayment(paymentId: string): Promise<PaymentAllocation[]> {\n return await this.list({\n where: { paymentId },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n /**\n * Find allocations by invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Array of allocations\n */\n async findByInvoice(invoiceId: string): Promise<PaymentAllocation[]> {\n return await this.list({\n where: { invoiceId },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n /**\n * Get total amount allocated to an invoice\n *\n * @param invoiceId - Invoice ID\n * @returns Total allocated amount\n */\n async getTotalAllocatedToInvoice(invoiceId: string): Promise<number> {\n const allocations = await this.findByInvoice(invoiceId);\n return allocations.reduce((sum, alloc) => sum + alloc.amount, 0);\n }\n\n /**\n * Get total amount allocated from a payment\n *\n * @param paymentId - Payment ID\n * @returns Total allocated amount\n */\n async getTotalAllocatedFromPayment(paymentId: string): Promise<number> {\n const allocations = await this.findByPayment(paymentId);\n return allocations.reduce((sum, alloc) => sum + alloc.amount, 0);\n }\n\n /**\n * Get remaining unallocated amount from a payment.\n *\n * @param paymentId - Payment ID\n * @param paymentAmount - Total payment amount\n * @returns Unallocated amount\n * @throws Error if payment is over-allocated (indicates data integrity issue)\n */\n async getUnallocatedFromPayment(\n paymentId: string,\n paymentAmount: number,\n ): Promise<number> {\n const allocated = await this.getTotalAllocatedFromPayment(paymentId);\n const remaining = paymentAmount - allocated;\n\n if (remaining < 0) {\n throw new Error(\n `Over-allocated payment detected for paymentId=${paymentId}: ` +\n `allocated amount (${allocated}) exceeds payment amount (${paymentAmount})`,\n );\n }\n\n return remaining;\n }\n\n /**\n * Find allocation by payment and invoice\n *\n * @param paymentId - Payment ID\n * @param invoiceId - Invoice ID\n * @returns Allocation or null\n */\n async findByPaymentAndInvoice(\n paymentId: string,\n invoiceId: string,\n ): Promise<PaymentAllocation | null> {\n const results = await this.list({\n where: { paymentId, invoiceId },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Find allocations by allocator\n *\n * @param allocatedBy - User/agent ID who made the allocation\n * @returns Array of allocations\n */\n async findByAllocator(allocatedBy: string): Promise<PaymentAllocation[]> {\n return await this.list({\n where: { allocatedBy },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n /**\n * Find allocations in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of allocations\n */\n async findByDateRange(\n startDate: Date,\n endDate: Date,\n ): Promise<PaymentAllocation[]> {\n return await this.list({\n where: {\n 'allocatedAt >=': startDate.toISOString(),\n 'allocatedAt <=': endDate.toISOString(),\n },\n orderBy: 'allocatedAt DESC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all payment allocations belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of payment allocations for the tenant\n */\n async findByTenant(tenantId: string): Promise<PaymentAllocation[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payment allocations (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 payment allocations\n */\n async findGlobal(): Promise<PaymentAllocation[]> {\n return queryGlobal<PaymentAllocation>(this);\n }\n\n /**\n * Find payment allocations for a tenant including global allocations.\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 payment allocations\n */\n async findWithGlobals(tenantId: string): Promise<PaymentAllocation[]> {\n return queryWithGlobals<PaymentAllocation>(\n this,\n tenantId,\n 'PaymentAllocation.findWithGlobals',\n );\n }\n}\n","/**\n * Payment model - tracks payments with ledger integration\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Journal } from '@happyvertical/smrt-ledgers';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n PaymentMethod,\n type PaymentOptions,\n PaymentStatus,\n type RecordPaymentOptions,\n} from '../types/index.js';\n\n/**\n * Legal status transitions for a Payment, keyed by the prior persisted status.\n * A status mapping to itself (no-op re-save) is always permitted and handled\n * separately. Transitioning *into* COMPLETED is additionally gated on the\n * verified `recordPayment()` settlement path (see {@link Payment.save}) — it is\n * never reachable by raw mass-assignment even though it appears here as a\n * structurally legal edge out of PENDING.\n *\n * Forward path: PENDING → COMPLETED, with FAILED / CANCELLED as alternate\n * terminal exits from PENDING and REFUNDED reachable from COMPLETED.\n */\nconst PAYMENT_STATUS_TRANSITIONS: Record<PaymentStatus, PaymentStatus[]> = {\n [PaymentStatus.PENDING]: [\n PaymentStatus.COMPLETED,\n PaymentStatus.FAILED,\n PaymentStatus.CANCELLED,\n ],\n // A completed payment can only be reversed via refund.\n [PaymentStatus.COMPLETED]: [PaymentStatus.REFUNDED],\n // Terminal states.\n [PaymentStatus.FAILED]: [],\n [PaymentStatus.REFUNDED]: [],\n [PaymentStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each Payment instance was loaded with,\n * so the save-time transition guard can compare the prior persisted status\n * against the one being written without adding a persisted column. WeakMap\n * keeps it out of the schema and GCs with the instance — same pattern as the\n * other commerce models.\n */\nconst loadedPaymentStatus = new WeakMap<Payment, PaymentStatus>();\n\n/**\n * Marks instances whose transition into COMPLETED is being driven by the\n * verified `recordPayment()` settlement path (which posts a balanced journal\n * before flipping the status). The save-time guard consults this set so that\n * COMPLETED can only be reached through that path, never via raw\n * mass-assignment on the generated update route. Cleared after the save runs.\n */\nconst settlementInProgress = new WeakSet<Payment>();\n\n/**\n * Payment represents a financial transaction against a contract.\n *\n * Payments can be integrated with smrt-ledgers to automatically\n * create balanced journal entries for proper accounting.\n *\n * @example\n * ```typescript\n * // Create a payment\n * const payment = await payments.create({\n * contractId: order.id,\n * customerId: customer.id,\n * amount: 1500.00,\n * method: PaymentMethod.CREDIT_CARD,\n * transactionId: 'stripe_pi_123456'\n * });\n *\n * // Record with ledger integration\n * await payment.recordPayment({\n * ledgerId: ledger.id,\n * receivablesAccountId: arAccount.id,\n * cashAccountId: bankAccount.id\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // ROOT FIX (S5 audit #1390 round 4): restrict the generated create/update\n // write surface so privileged / settlement-derived fields can NEVER be set\n // by a generated REST/MCP route — closing the status-mass-assignment vector\n // at the surface rather than relying solely on the save()-time guard.\n //\n // `writable` is an allowlist: only these fields survive applyWritablePolicy()\n // on BOTH create and update (#1540). Deliberately EXCLUDED:\n // - `status` — a COMPLETED Payment is settlement proof downstream\n // (PaymentIntent's PAID verification trusts it). It is only ever set by\n // the verified `recordPayment()` settlement path, never by a caller. This\n // closes the \"forged COMPLETED Payment via api.create\" vector (codex HIGH#1).\n // - `journalId` / `paidAt` — settlement-derived, written by recordPayment().\n // - `syncedAt` — provider-sync bookkeeping, written by the sync path.\n api: {\n include: ['list', 'get', 'create', 'update'],\n writable: [\n 'contractId',\n 'customerId',\n 'amount',\n 'currency',\n 'method',\n 'transactionId',\n 'reference',\n 'notes',\n 'externalId',\n 'externalProvider',\n 'backendId',\n 'backendTxRef',\n 'nativeAmount',\n 'nativeCurrency',\n 'usdAtQuote',\n 'usdAtConfirmation',\n ],\n },\n // NOTE: `recordPayment` is intentionally NOT exposed over MCP. It posts a\n // balanced journal into smrt-ledgers (moves money in the books) and flips\n // the payment to COMPLETED — not safe as an unguarded MCP tool that carries\n // no authz. Financial mutations must go through application code that\n // enforces permissions. The MCP create/update tools honor the same\n // `api.writable` allowlist above, so `status` is unsettable over MCP too.\n // See S5 audit #1390.\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Payment extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global payments\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Contract this payment is for\n */\n @foreignKey('Contract')\n contractId: string = '';\n\n /**\n * Customer who made the payment\n */\n @foreignKey('Customer')\n customerId: string = '';\n\n /**\n * Payment amount\n */\n amount: number = 0.0;\n\n /**\n * Currency code (ISO 4217)\n */\n currency: string = 'USD';\n\n /**\n * Payment method used\n */\n method: PaymentMethod = PaymentMethod.BANK_TRANSFER;\n\n /**\n * Current payment status\n */\n status: PaymentStatus = PaymentStatus.PENDING;\n\n /**\n * External transaction ID (from payment processor)\n */\n transactionId: string = '';\n\n /**\n * Internal reference number\n */\n reference: string = '';\n\n /**\n * Link to smrt-ledgers Journal (created by recordPayment)\n */\n @crossPackageRef('@happyvertical/smrt-ledgers:Journal')\n journalId: string = '';\n\n /**\n * When the payment was completed\n */\n paidAt: Date | null = null;\n\n /**\n * Notes about the payment\n */\n notes: string = '';\n\n // ============================================================================\n // Provider Sync\n // ============================================================================\n\n /**\n * External ID in accounting provider (e.g., QBO payment ID)\n */\n externalId: string = '';\n\n /**\n * Accounting provider name ('quickbooks' | 'stripe' | 'paypal' | etc.)\n */\n externalProvider: string = '';\n\n /**\n * When payment was last synced to provider\n */\n syncedAt: Date | null = null;\n\n // ============================================================================\n // Payment Backend Identity\n // ============================================================================\n //\n // When a `PaymentBackend` adapter (Stripe, x402, BTC RPC, etc.) brokers the\n // payment rather than a human recording it manually, we capture the\n // backend identity, its native-currency amount, and the USD valuation at\n // both quote and confirmation time. The classic `amount` / `currency`\n // pair stays as the canonical settlement number; the backend fields\n // describe *how* the funds arrived and let downstream accounting close\n // the loop on volatile-currency drift.\n //\n // All fields default to empty / zero so existing Payment consumers that\n // pre-date the marketplace adapter machinery are unaffected.\n\n /**\n * Stable identifier of the `PaymentBackend` adapter that served this\n * payment — e.g. `base-usdc`, `solana-usdc`, `btc`, `stripe`, `paypal`.\n *\n * Distinct from {@link externalProvider}: `backendId` names the SMRT\n * payment-rail adapter, while `externalProvider` names a downstream\n * accounting destination (QuickBooks, Stripe-the-accounting-source).\n * The same Stripe payment will have `backendId: 'stripe'` AND\n * `externalProvider: 'stripe'`; a crypto payment synced to QBO will\n * have `backendId: 'base-usdc'` and `externalProvider: 'quickbooks'`.\n */\n backendId: string = '';\n\n /**\n * Chain transaction hash or backend aggregator's reference id. For\n * on-chain payments this is the tx hash (`0x...` on EVM, base58 sig on\n * Solana, txid on Bitcoin); for fiat-rail backends it's the gateway's\n * own reference. Kept distinct from {@link transactionId} so consumers\n * that already populate `transactionId` with a provider-internal id\n * don't have to overload it.\n */\n backendTxRef: string = '';\n\n /**\n * The amount the backend actually moved, in its own native currency.\n * For stablecoin rails this typically equals `amount`; for volatile-\n * currency rails (BTC, ETH) it's the satoshi/wei figure that the chain\n * recorded, independent of any USD valuation.\n */\n nativeAmount: number = 0.0;\n\n /**\n * Code identifying the native currency `nativeAmount` is denominated\n * in. Mirrors the `backendId` namespacing convention — `USDC-base`,\n * `BTC`, `ETH`, `USD-stripe`. Empty string means \"use {@link currency}\"\n * (i.e. the payment was already quoted and settled in the same\n * currency).\n */\n nativeCurrency: string = '';\n\n /**\n * USD valuation of the payment at the moment the price was quoted to\n * the buyer (typically the moment a `PaymentIntent` was issued).\n * Stored at decimal precision; empty default `0.0` means no USD-quote\n * snapshot was taken (the payment was already USD-denominated or the\n * backend doesn't require drift accounting).\n */\n usdAtQuote: number = 0.0;\n\n /**\n * USD valuation of the payment at the moment it was confirmed on the\n * backend (chain confirmation, gateway settlement, etc.). The delta\n * between {@link usdAtQuote} and `usdAtConfirmation` is the USD drift\n * the operator absorbs (positive or negative) when accepting payment\n * in a volatile native currency.\n */\n usdAtConfirmation: number = 0.0;\n\n constructor(options: PaymentOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.customerId !== undefined) this.customerId = options.customerId;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.method !== undefined) this.method = options.method;\n if (options.status !== undefined) this.status = options.status;\n if (options.transactionId !== undefined)\n this.transactionId = options.transactionId;\n if (options.reference !== undefined) this.reference = options.reference;\n if (options.journalId !== undefined) this.journalId = options.journalId;\n if (options.paidAt !== undefined) this.paidAt = options.paidAt;\n if (options.notes !== undefined) this.notes = options.notes;\n if (options.externalId !== undefined) this.externalId = options.externalId;\n if (options.externalProvider !== undefined)\n this.externalProvider = options.externalProvider;\n if (options.syncedAt !== undefined) this.syncedAt = options.syncedAt;\n if (options.backendId !== undefined) this.backendId = options.backendId;\n if (options.backendTxRef !== undefined)\n this.backendTxRef = options.backendTxRef;\n if (options.nativeAmount !== undefined)\n this.nativeAmount = options.nativeAmount;\n if (options.nativeCurrency !== undefined)\n this.nativeCurrency = options.nativeCurrency;\n if (options.usdAtQuote !== undefined) this.usdAtQuote = options.usdAtQuote;\n if (options.usdAtConfirmation !== undefined)\n this.usdAtConfirmation = options.usdAtConfirmation;\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 the generated update route, a stale caller,\n * etc.). Freshly-constructed (not-yet-saved) payments have no prior status.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n if (await this.isSaved()) {\n loadedPaymentStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Save-time state-machine guard (S5 audit #1390).\n *\n * `status` is mass-assignable on the generated update/create routes, and a\n * COMPLETED Payment is treated as settlement proof downstream — e.g.\n * {@link PaymentIntent}'s PAID verification trusts a COMPLETED Payment row.\n * A forged `status: 'completed'` (with arbitrary amounts and no journal)\n * would therefore satisfy that check without any money having moved.\n *\n * This guard enforces two things:\n * - **Transitions must be legal** per {@link PAYMENT_STATUS_TRANSITIONS}.\n * - **Reaching COMPLETED requires the verified settlement path — on\n * creation AND on update.** Only `recordPayment()` (which posts a\n * balanced journal and links `journalId`) may write a Payment into\n * COMPLETED; it announces itself via {@link settlementInProgress}. A raw\n * `status: 'completed'` is rejected whether the row is brand-new (a\n * GENESIS `create({ status: 'completed' })`) or already persisted — that\n * is the exact path a forged COMPLETED would take to satisfy\n * PaymentIntent's PAID verification without any money having moved (codex\n * HIGH#1, #1390 round 5). There is deliberately NO import/fixture\n * carve-out: a COMPLETED Payment is settlement proof downstream, so it is\n * only ever reachable through `recordPayment()`, on every surface\n * (REST/MCP/CLI/direct). Fixtures/migrations that need a completed payment\n * must drive it through `recordPayment()` (or start non-COMPLETED).\n */\n override async save(): Promise<this> {\n const priorRow = await this.loadPersistedRow();\n const prior =\n priorRow && priorRow.status != null\n ? (priorRow.status as PaymentStatus)\n : loadedPaymentStatus.get(this);\n this.assertStatusTransition(prior);\n\n // Reaching COMPLETED is only legal via the verified settlement path,\n // regardless of whether this is a create (no persisted prior) or an\n // update of an existing row. A GENESIS create with status: COMPLETED would\n // otherwise forge settlement proof, so we close it here at the model level\n // — this covers EVERY surface (REST/MCP/CLI/direct), independent of any\n // writable-allowlist gap on a given surface.\n const reachingCompleted =\n this.status === PaymentStatus.COMPLETED &&\n prior !== PaymentStatus.COMPLETED;\n if (reachingCompleted && !settlementInProgress.has(this)) {\n throw new Error(\n `Payment ${this.id || '<new>'}: cannot set status to COMPLETED via ` +\n 'raw assignment — a Payment is only completed through ' +\n 'recordPayment(), which posts a balanced settlement journal. Use ' +\n 'recordPayment() instead of setting status directly.',\n );\n }\n\n // Freeze the settled monetary fields once the row is already COMPLETED in\n // the database (S5 audit #1390 round 6 — codex ROOT INSIGHT; mirrors the\n // PaymentIntent PAID backing-field freeze). A COMPLETED Payment is\n // settlement proof: its amount was reconciled against a balanced journal\n // and is trusted downstream — PaymentIntent's PAID verification binds the\n // winning option's `nativeAmount` to `Payment.amount` / `nativeAmount`. The\n // only legal exit from COMPLETED is COMPLETED → REFUNDED, and a refund must\n // NOT alter the settled figures. Closing this at the model level covers\n // EVERY surface (REST/MCP/CLI/direct), independent of the per-surface\n // writable allowlist (`amount`/`currency`/`nativeAmount`/... are writable\n // on a *new* PENDING payment, but must be immutable once settled).\n if (priorRow && priorRow.status === PaymentStatus.COMPLETED) {\n this.assertSettledAmountsUnchanged(priorRow);\n }\n\n try {\n const result = (await super.save()) as this;\n loadedPaymentStatus.set(this, this.status);\n return result;\n } finally {\n settlementInProgress.delete(this);\n }\n }\n\n /**\n * Reject an illegal status flip. Compares the about-to-be-written status\n * against the status the row was loaded with. No-op transitions (status\n * unchanged) and brand-new rows (no prior) are always allowed — the\n * COMPLETED-specific settlement requirement is enforced separately in\n * {@link save}.\n */\n private assertStatusTransition(prior: PaymentStatus | undefined): void {\n if (prior === undefined) return; // new row — any starting status (subject to the COMPLETED gate)\n if (prior === this.status) return; // no-op re-save\n const allowed = PAYMENT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Payment ${this.id}: illegal status transition '${prior}' → '${this.status}'. ` +\n 'Use the guarded helpers (recordPayment / markFailed / cancel).',\n );\n }\n }\n\n /**\n * Load the AUTHORITATIVE persisted row (S5 audit #1390 round 4, codex HIGH#1;\n * extended round 6 to carry the full row, not just `status`). The WeakMap is\n * only populated when {@link initialize} loaded the row from the DB; it is\n * empty for an instance built via\n * `collection.create({ id: <existing>, _skipLoad: true })` — the upsert path\n * that lets a caller write onto an existing row without hydrating it. Trusting\n * an empty WeakMap there would treat the write as a brand-new row and skip the\n * transition guard entirely (a poisonable prior-state).\n *\n * So when this instance carries an `id`, read the persisted row straight from\n * the database and use it as the prior — a create-onto-existing is an update.\n * Returns `undefined` when no row exists (truly new), so callers fall back to\n * the WeakMap (which is also empty then). Settled-amount columns are\n * snake_case (`native_amount`, etc.); the `status` column is single-word.\n */\n private async loadPersistedRow(): Promise<\n Record<string, unknown> | undefined\n > {\n if (!this.id) return undefined;\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n return row ?? undefined;\n } catch {\n // DB not ready / table absent — treat as new (in-memory fallback in save).\n return undefined;\n }\n }\n\n /**\n * Reject any change to the settled monetary fields of an already-COMPLETED\n * Payment (S5 audit #1390 round 6 — codex ROOT INSIGHT). Mirrors the\n * PaymentIntent PAID backing-field freeze. A COMPLETED Payment's economic\n * identity is settled: the `amount` was posted into a balanced journal and is\n * the figure PaymentIntent's PAID verification reconciles against, so it (and\n * the native/USD valuation fields that describe the same money) must not\n * drift. The only legal status move out of COMPLETED is REFUNDED, which\n * reverses the funds without rewriting how much they were. Compared against\n * the AUTHORITATIVE persisted row so the freeze holds on every surface, even\n * for an un-hydrated create-onto-existing upsert.\n */\n private assertSettledAmountsUnchanged(\n priorRow: Record<string, unknown>,\n ): void {\n const frozen: Array<[string, number | string, number | string]> = [\n ['amount', Number(priorRow.amount ?? 0), Number(this.amount ?? 0)],\n [\n 'currency',\n String(priorRow.currency ?? ''),\n String(this.currency ?? ''),\n ],\n [\n 'nativeAmount',\n Number(priorRow.native_amount ?? 0),\n Number(this.nativeAmount ?? 0),\n ],\n [\n 'nativeCurrency',\n String(priorRow.native_currency ?? ''),\n String(this.nativeCurrency ?? ''),\n ],\n [\n 'usdAtQuote',\n Number(priorRow.usd_at_quote ?? 0),\n Number(this.usdAtQuote ?? 0),\n ],\n [\n 'usdAtConfirmation',\n Number(priorRow.usd_at_confirmation ?? 0),\n Number(this.usdAtConfirmation ?? 0),\n ],\n ];\n for (const [field, prior, next] of frozen) {\n if (prior !== next) {\n throw new Error(\n `Payment ${this.id}: '${field}' is frozen once the payment is ` +\n `COMPLETED (was '${prior}', got '${next}'). A settled payment's ` +\n 'monetary fields cannot be mutated — issue a refund instead.',\n );\n }\n }\n }\n\n /**\n * USD drift between quote time and confirmation time — what the\n * operator gained (positive) or lost (negative) by accepting a\n * volatile-currency payment. Returns `0` when either side of the\n * comparison is missing or zero, so callers don't have to special-case\n * fiat-rail / stablecoin payments.\n */\n usdDrift(): number {\n if (!this.usdAtQuote || !this.usdAtConfirmation) return 0;\n return this.usdAtConfirmation - this.usdAtQuote;\n }\n\n /**\n * Check if payment is pending\n */\n isPending(): boolean {\n return this.status === PaymentStatus.PENDING;\n }\n\n /**\n * Check if payment is completed\n */\n isCompleted(): boolean {\n return this.status === PaymentStatus.COMPLETED;\n }\n\n /**\n * Check if payment is refunded\n */\n isRefunded(): boolean {\n return this.status === PaymentStatus.REFUNDED;\n }\n\n /**\n * Record the payment and create a balanced journal entry.\n *\n * Creates a journal entry in smrt-ledgers:\n * - Debit: Cash/Bank account (assets increase)\n * - Credit: Accounts Receivable (receivables decrease)\n *\n * @param options - Ledger and account configuration\n * @returns The created journal\n *\n * @example\n * ```typescript\n * const journal = await payment.recordPayment({\n * ledgerId: ledger.id,\n * receivablesAccountId: arAccount.id,\n * cashAccountId: bankAccount.id\n * });\n * console.log(`Created journal: ${journal.number}`);\n * ```\n */\n async recordPayment(options: RecordPaymentOptions): Promise<Journal> {\n if (this.status === PaymentStatus.COMPLETED) {\n throw new Error('Payment already recorded');\n }\n\n if (this.amount <= 0) {\n throw new Error('Payment amount must be positive');\n }\n\n // Dynamic import to avoid hard dependency on smrt-ledgers\n const { JournalCollection } = await import('@happyvertical/smrt-ledgers');\n\n // Create the journal collection\n const journalCollection = await JournalCollection.create(this.options);\n\n // Create a new journal for this payment\n const journal = await journalCollection.create({\n date: new Date(),\n description: `Payment received for contract ${this.contractId}`,\n sourceModule: 'smrt-commerce',\n sourceRef: this.id,\n });\n await journal.save();\n\n // Add balanced entries:\n // Debit Cash/Bank (assets increase)\n await journal.addEntry({\n accountId: options.cashAccountId,\n debit: this.amount,\n memo: `Payment ${this.reference || this.id}`,\n });\n\n // Credit Accounts Receivable (receivables decrease)\n await journal.addEntry({\n accountId: options.receivablesAccountId,\n credit: this.amount,\n memo: `Payment ${this.reference || this.id}`,\n });\n\n // Post the journal (validates balance and finalizes)\n await journal.post();\n\n // Update payment record. Announce the verified settlement to the save-time\n // guard so the COMPLETED transition is accepted (the guard rejects any\n // other route into COMPLETED). A posted journal always carries an id;\n // fail fast rather than persist an empty-string FK if that invariant breaks.\n if (!journal.id) {\n throw new Error(\n 'Payment.recordPayment: journal was posted but has no id',\n );\n }\n this.journalId = journal.id;\n this.status = PaymentStatus.COMPLETED;\n this.paidAt = new Date();\n settlementInProgress.add(this);\n await this.save();\n\n return journal;\n }\n\n /**\n * Get the linked journal entry (if recorded)\n */\n async getJournal(): Promise<Journal | null> {\n if (!this.journalId) 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.journalId });\n } catch {\n // smrt-ledgers not available\n return null;\n }\n }\n\n /**\n * Mark payment as failed\n */\n markFailed(reason?: string): void {\n this.status = PaymentStatus.FAILED;\n if (reason) {\n this.notes = `${this.notes ? `${this.notes}\\n` : ''}Failed: ${reason}`;\n }\n }\n\n /**\n * Cancel the payment\n */\n cancel(): void {\n if (this.status === PaymentStatus.COMPLETED) {\n throw new Error('Cannot cancel a completed payment. Use refund instead.');\n }\n this.status = PaymentStatus.CANCELLED;\n }\n}\n\nexport default Payment;\n","/**\n * PaymentCollection - Collection manager for Payment objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Payment } from '../models/Payment.js';\nimport { type PaymentMethod, PaymentStatus } from '../types/index.js';\n\nexport class PaymentCollection extends SmrtCollection<Payment> {\n static readonly _itemClass = Payment;\n\n /**\n * Find payments by contract\n *\n * @param contractId - Contract ID\n * @returns Array of payments\n */\n async findByContract(contractId: string): Promise<Payment[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find payments by customer\n *\n * @param customerId - Customer ID\n * @returns Array of payments\n */\n async findByCustomer(customerId: string): Promise<Payment[]> {\n return await this.list({\n where: { customerId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find payments by status\n *\n * @param status - Payment status\n * @returns Array of payments\n */\n async findByStatus(status: PaymentStatus): Promise<Payment[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find payments by method\n *\n * @param method - Payment method\n * @returns Array of payments\n */\n async findByMethod(method: PaymentMethod): Promise<Payment[]> {\n return await this.list({\n where: { method },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all pending payments\n *\n * @returns Array of pending payments\n */\n async findPending(): Promise<Payment[]> {\n return await this.findByStatus(PaymentStatus.PENDING);\n }\n\n /**\n * Find all completed payments\n *\n * @returns Array of completed payments\n */\n async findCompleted(): Promise<Payment[]> {\n return await this.findByStatus(PaymentStatus.COMPLETED);\n }\n\n /**\n * Find payment by transaction ID\n *\n * @param transactionId - External transaction ID\n * @returns Payment or null\n */\n async findByTransactionId(transactionId: string): Promise<Payment | null> {\n const results = await this.list({\n where: { transactionId },\n limit: 1,\n });\n return results[0] || null;\n }\n\n /**\n * Find payments in date range\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @returns Array of payments\n */\n async findByDateRange(startDate: Date, endDate: Date): Promise<Payment[]> {\n return await this.list({\n where: {\n 'paidAt >=': startDate.toISOString(),\n 'paidAt <=': endDate.toISOString(),\n },\n orderBy: 'paidAt DESC',\n });\n }\n\n /**\n * Calculate total payments for a contract\n *\n * @param contractId - Contract ID\n * @returns Total amount paid\n */\n async getTotalForContract(contractId: string): Promise<number> {\n const payments = await this.list({\n where: {\n contractId,\n status: PaymentStatus.COMPLETED,\n },\n });\n\n return payments.reduce((sum, payment) => sum + payment.amount, 0);\n }\n\n /**\n * Find payments linked to a ledger journal\n *\n * @param journalId - Journal ID from smrt-ledgers\n * @returns Payment or null\n */\n async findByJournal(journalId: string): Promise<Payment | null> {\n const results = await this.list({\n where: { journalId },\n limit: 1,\n });\n return results[0] || null;\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all payments belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of payments for the tenant\n */\n async findByTenant(tenantId: string): Promise<Payment[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payments (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 payments\n */\n async findGlobal(): Promise<Payment[]> {\n return queryGlobal<Payment>(this);\n }\n\n /**\n * Find payments for a tenant including global payments.\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 payments\n */\n async findWithGlobals(tenantId: string): Promise<Payment[]> {\n return queryWithGlobals<Payment>(this, tenantId, 'Payment.findWithGlobals');\n }\n}\n","/**\n * PaymentIntent — short-lived pre-payment commitment with multi-option\n * semantics.\n *\n * A PaymentIntent is a one-shot quote that locks a USD price for a fixed\n * window and lists one or more `PaymentOption`s describing different\n * payment rails (USDC-on-Base, BTC, Stripe, etc.) that can each satisfy\n * the intent. The first option that receives an incoming payment wins —\n * the intent transitions to {@link PaymentIntentStatus.PAID} and the\n * other options are implicitly retired. Later inbound funds to a\n * retired option must be flagged for refund by the consumer's\n * payment-routing layer.\n *\n * Distinct from `Estimate` (a long-form contract proposal STI subtype\n * of `Contract`): a `PaymentIntent` is short-lived (minutes, not days),\n * carries no line items, and exists to coordinate the moment-of-payment.\n *\n * Industry-neutral: any application that offers more than one route to\n * pay for the same thing — marketplaces, billing systems with multi-\n * provider checkout, agent-driven (x402) flows — benefits.\n *\n * @packageDocumentation\n */\n\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type PaymentIntentOptions,\n PaymentIntentStatus,\n type PaymentOption,\n PaymentStatus,\n} from '../types/index.js';\n\n/**\n * Sub-cent rounding tolerance for native-amount reconciliation between the\n * winning PaymentOption and the referenced Payment row.\n */\nconst PAYMENT_INTENT_EPSILON = 0.01;\n\n/**\n * Legal status transitions for a PaymentIntent, keyed by the prior persisted\n * status. Mirrors the state machine documented on {@link PaymentIntentStatus}\n * and enforced by the dedicated helpers, so a raw `status` mass-assignment on\n * the generated update route can't skip steps (e.g. `awaiting_payment →\n * issued`) or revive a terminal state.\n *\n * `awaiting_payment → paid → (issued | retired)`, with `expired` / `cancelled`\n * as alternate terminal exits from `awaiting_payment`. `issued` may still be\n * `retired` (a reversal after rights were issued).\n */\nconst PAYMENT_INTENT_STATUS_TRANSITIONS: Record<\n PaymentIntentStatus,\n PaymentIntentStatus[]\n> = {\n [PaymentIntentStatus.AWAITING_PAYMENT]: [\n PaymentIntentStatus.PAID,\n PaymentIntentStatus.EXPIRED,\n PaymentIntentStatus.CANCELLED,\n ],\n [PaymentIntentStatus.PAID]: [\n PaymentIntentStatus.ISSUED,\n PaymentIntentStatus.RETIRED,\n ],\n [PaymentIntentStatus.ISSUED]: [PaymentIntentStatus.RETIRED],\n // Terminal states.\n [PaymentIntentStatus.RETIRED]: [],\n [PaymentIntentStatus.EXPIRED]: [],\n [PaymentIntentStatus.CANCELLED]: [],\n};\n\n/**\n * Module-scoped record of the status each PaymentIntent was loaded with, so\n * the save-time guard can detect a status being forced to PAID via raw\n * mass-assignment (bypassing the verified transition path). Same WeakMap\n * pattern as the rest of the package.\n */\nconst loadedIntentStatus = new WeakMap<PaymentIntent, PaymentIntentStatus>();\n\n/**\n * Default price-lock window — 15 minutes is a reasonable middle ground:\n * long enough for a buyer to swap wallets / approve a Stripe payment,\n * short enough that volatile-currency drift stays bounded.\n */\nconst DEFAULT_PRICE_LOCK_WINDOW_MS = 15 * 60 * 1000;\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Natural-key idempotency: a consumer that retries `create` with the\n // same (offeringRef, licenseeEmail, idempotencyKey) within the\n // retention window will hit the existing row via upsert rather than\n // creating a duplicate. Tenant id is part of the key so the same\n // idempotency key never collides across tenants.\n conflictColumns: [\n 'tenant_id',\n 'offering_ref',\n 'licensee_email',\n 'idempotency_key',\n ],\n // ROOT FIX (S5 audit #1390 round 4): the generated create/update surface may\n // only set the quote-definition fields. The PAID-state backing fields\n // (`status`, `paymentId`, `paidOptionBackendId`) are EXCLUDED — they are set\n // exclusively by the verified `verifyAndMarkPaid()` / `markIssued()` path, so\n // a caller can never forge a PAID intent (or repoint one) through a generated\n // route. `paymentOptions` IS writable (a quote may be re-priced while\n // AWAITING_PAYMENT) but is frozen by `save()` once the intent is PAID/ISSUED\n // (codex HIGH#2). Timestamps + priceLockExpiresAt are derived and excluded.\n api: {\n include: ['list', 'get', 'create', 'update'],\n writable: [\n 'skuId',\n 'offeringRef',\n 'licenseeEmail',\n 'customerId',\n 'paymentOptions',\n 'usdPriceLocked',\n 'priceLockWindowMs',\n 'idempotencyKey',\n 'notes',\n ],\n },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class PaymentIntent extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation. Nullable so the same model\n * can serve both per-tenant SaaS deployments and single-tenant /\n * global setups.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Plain string reference to the upstream `Sku` (from\n * `@happyvertical/smrt-products`) being purchased. Cross-package\n * reference — plain string, not `@foreignKey()`, to avoid the\n * circular dependency the framework warns against.\n *\n * Required for the marketplace flow; other consumers can leave it\n * empty if they don't model a catalog.\n */\n skuId: string = '';\n\n /**\n * Caller-supplied scope for the idempotency-key natural key. The\n * marketplace sets this to a `Sku` id, but the field is intentionally\n * abstract so other consumers (subscription tiers, donation tiers,\n * tipping flows) can scope idempotency by whatever granularity makes\n * sense.\n *\n * Empty string is permitted but disables the natural-key dedup —\n * every retry then creates a fresh row.\n */\n offeringRef: string = '';\n\n /**\n * The buyer's email address. The licensee on any downstream\n * `LicenseSale` (or similar rights-issuance row) is identified by\n * this email — high-tier purchases also create a `Customer` record\n * and set {@link customerId}, but most purchases get away with email\n * alone.\n */\n licenseeEmail: string = '';\n\n /**\n * Optional link to a `Customer` row for purchases that warrant a\n * full customer record (high-tier licensing, recurring buyers,\n * etc.). Cross-model reference is kept as a plain string to match\n * the rest of the package's cross-package convention.\n */\n customerId: string = '';\n\n /**\n * The payment rails the buyer can choose between to satisfy this\n * intent. Stored as a JSON column. See {@link PaymentOption} for\n * the per-option shape. Empty array means the intent is invalid /\n * mis-built; callers should reject save in that case.\n */\n paymentOptions: PaymentOption[] = [];\n\n /**\n * USD price locked at quote time. Stored at decimal precision.\n * Independent of any specific option's native-currency amount;\n * `usdPriceLocked` is the canonical \"what this costs in USD\"\n * number used downstream for reporting, tax, and drift accounting.\n */\n usdPriceLocked: number = 0.0;\n\n /**\n * Length of the price-lock window in milliseconds. Defaults to 15\n * minutes; consumers can override per-intent. Stored so a later\n * audit can see the original window even after {@link priceLockExpiresAt}\n * has passed.\n */\n priceLockWindowMs: number = DEFAULT_PRICE_LOCK_WINDOW_MS;\n\n /**\n * Wall-clock time at which the price lock expires. Set by the\n * constructor (or by `expire()` if you want to short-circuit a\n * specific intent). Once `Date.now()` passes this value and the\n * status is still `AWAITING_PAYMENT`, callers should treat the\n * intent as expired and refuse to record a payment against it.\n */\n priceLockExpiresAt: Date | null = null;\n\n /**\n * Current status — see {@link PaymentIntentStatus} for the state\n * machine. Mutate via the dedicated `markPaid` / `markIssued` /\n * `expire` / `cancel` / `retire` helpers rather than assigning\n * directly, so the helpers' invariant checks run.\n */\n status: PaymentIntentStatus = PaymentIntentStatus.AWAITING_PAYMENT;\n\n /**\n * Caller-supplied idempotency key. Combined with `tenantId`,\n * `offeringRef`, and `licenseeEmail`, this forms the natural key\n * registered in `conflictColumns` — a retried `create` with the\n * same tuple upserts the existing row instead of creating a\n * duplicate.\n *\n * Empty string disables natural-key dedup (every retry creates a\n * fresh row).\n */\n idempotencyKey: string = '';\n\n /**\n * `backendId` of the option that satisfied the intent. Set by\n * {@link markPaid}; remains empty for non-paid intents. Used by\n * {@link isOptionRetired} to flag inbound funds on the other\n * options as \"needs refund\".\n */\n paidOptionBackendId: string = '';\n\n /**\n * Plain string reference to the `Payment` row that satisfied the\n * intent. Cross-model reference matches the rest of the\n * package's convention.\n */\n paymentId: string = '';\n\n /**\n * When the intent transitioned to `PAID`.\n */\n paidAt: Date | null = null;\n\n /**\n * When the intent transitioned to `ISSUED`.\n */\n issuedAt: Date | null = null;\n\n /**\n * When the intent transitioned to `EXPIRED`.\n */\n expiredAt: Date | null = null;\n\n /**\n * When the intent transitioned to `CANCELLED`.\n */\n cancelledAt: Date | null = null;\n\n /**\n * When the intent transitioned to `RETIRED`.\n */\n retiredAt: Date | null = null;\n\n /**\n * Optional human-readable notes (e.g., support tickets, refund\n * memos, cancellation reasons). Append-only by convention.\n */\n notes: string = '';\n\n constructor(options: PaymentIntentOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.skuId !== undefined) this.skuId = options.skuId;\n if (options.offeringRef !== undefined)\n this.offeringRef = options.offeringRef;\n if (options.licenseeEmail !== undefined)\n this.licenseeEmail = options.licenseeEmail;\n if (options.customerId !== undefined) this.customerId = options.customerId;\n if (options.paymentOptions !== undefined)\n this.paymentOptions = PaymentIntent.normalizePaymentOptions(\n options.paymentOptions,\n );\n if (options.usdPriceLocked !== undefined)\n this.usdPriceLocked = options.usdPriceLocked;\n if (options.priceLockWindowMs !== undefined)\n this.priceLockWindowMs = options.priceLockWindowMs;\n if (options.priceLockExpiresAt !== undefined) {\n this.priceLockExpiresAt = PaymentIntent.coerceDate(\n options.priceLockExpiresAt,\n );\n }\n if (options.status !== undefined) this.status = options.status;\n if (options.idempotencyKey !== undefined)\n this.idempotencyKey = options.idempotencyKey;\n if (options.paidOptionBackendId !== undefined)\n this.paidOptionBackendId = options.paidOptionBackendId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.paidAt !== undefined)\n this.paidAt = PaymentIntent.coerceDate(options.paidAt);\n if (options.issuedAt !== undefined)\n this.issuedAt = PaymentIntent.coerceDate(options.issuedAt);\n if (options.expiredAt !== undefined)\n this.expiredAt = PaymentIntent.coerceDate(options.expiredAt);\n if (options.cancelledAt !== undefined)\n this.cancelledAt = PaymentIntent.coerceDate(options.cancelledAt);\n if (options.retiredAt !== undefined)\n this.retiredAt = PaymentIntent.coerceDate(options.retiredAt);\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Re-normalize `paymentOptions` after the framework's\n * `initializePropertiesFromOptions` pass has overwritten the\n * constructor-set values with the raw cloned input, and stamp a\n * default `priceLockExpiresAt` so newly-created intents have a\n * concrete expiry without callers needing to compute it.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.paymentOptions = PaymentIntent.normalizePaymentOptions(\n this.paymentOptions as unknown,\n );\n this.priceLockExpiresAt = PaymentIntent.coerceDate(this.priceLockExpiresAt);\n this.paidAt = PaymentIntent.coerceDate(this.paidAt);\n this.issuedAt = PaymentIntent.coerceDate(this.issuedAt);\n this.expiredAt = PaymentIntent.coerceDate(this.expiredAt);\n this.cancelledAt = PaymentIntent.coerceDate(this.cancelledAt);\n this.retiredAt = PaymentIntent.coerceDate(this.retiredAt);\n if (!this.priceLockExpiresAt) {\n // Anchor the price-lock window to \"now\" if no explicit expiry\n // was supplied. Using the configured window so consumers that\n // override `priceLockWindowMs` get the right anchor.\n this.priceLockExpiresAt = new Date(\n Date.now() + (this.priceLockWindowMs || DEFAULT_PRICE_LOCK_WINDOW_MS),\n );\n }\n if (await this.isSaved()) {\n loadedIntentStatus.set(this, this.status);\n }\n return this;\n }\n\n /**\n * Save-time state-machine guard (S5 audit #1390 round 2).\n *\n * `status`, `paymentId`, and `paidOptionBackendId` are all mass-assignable on\n * the generated update route, and the sync `markPaid` helper trusts its\n * arguments. Two distinct attacks follow:\n *\n * 1. **Forge a PAID intent** — set `status: 'paid'` (or call bare `markPaid`)\n * against a bogus / pending / non-matching Payment. This falsifies\n * downstream \"this was paid for\" rights issuance.\n * 2. **Repoint an already-PAID intent** — leave `status: 'paid'` but swap\n * `paymentId` / `paidOptionBackendId` to a bogus Payment after the fact.\n * Round 1 only verified the *transition into* PAID, so an already-PAID\n * row could be silently repointed with no re-verify. Likewise an intent\n * forced straight to `ISSUED` (the terminal happy-path that gates rights\n * issuance) was never required to have been backed by a real Payment.\n *\n * This guard therefore:\n * - validates the status transition is legal (no `awaiting_payment → issued`\n * skips, no reviving terminal states);\n * - re-verifies the backing Payment on **every** save where the persisted\n * status is PAID or ISSUED — not just the transition edge — so a repoint\n * of the paid backing fields is caught.\n *\n * Re-verifying an unchanged PAID/ISSUED row is cheap (one Payment load) and\n * closes the repoint hole; freezing the backing fields would instead block\n * legitimate corrections, so we re-verify.\n */\n override async save(): Promise<this> {\n const priorRow = await this.loadPersistedRow();\n const prior = priorRow\n ? (priorRow.status as PaymentIntentStatus)\n : loadedIntentStatus.get(this);\n this.assertStatusTransition(prior);\n\n // Freeze the backing fields once the intent is already PAID/ISSUED in the\n // database (codex HIGH#2). Round 3 re-verified the backing Payment but did\n // so against the *mutable* in-memory `paymentOptions`, so a caller could\n // swap `paymentId` to a different completed Payment AND edit the matching\n // option's `nativeAmount` so the reconciliation still passed — silently\n // repointing a settled intent at unrelated funds. A PAID/ISSUED intent's\n // economic identity (paymentId + paidOptionBackendId + usdPriceLocked +\n // paymentOptions) is settled and must not change; corrections go through\n // `retire()` + a fresh intent.\n if (\n priorRow &&\n (priorRow.status === PaymentIntentStatus.PAID ||\n priorRow.status === PaymentIntentStatus.ISSUED)\n ) {\n this.assertBackingFieldsUnchanged(priorRow);\n }\n\n const persistingPaidOrIssued =\n this.status === PaymentIntentStatus.PAID ||\n this.status === PaymentIntentStatus.ISSUED;\n\n if (persistingPaidOrIssued) {\n await this.assertBackedByCompletedPayment();\n }\n\n const result = (await super.save()) as this;\n loadedIntentStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Load the authoritative persisted row for this intent (S5 audit #1390 round\n * 4). Returns `undefined` when the intent has no `id` or no row exists yet\n * (truly new). Reading the DB directly — rather than trusting the\n * {@link loadedIntentStatus} WeakMap, which is only populated when\n * {@link initialize} hydrated the row — defeats the poisonable-prior-state\n * vector where `create({ id: <existing>, _skipLoad: true })` produces an\n * un-hydrated instance whose WeakMap entry is missing. A create-onto-existing\n * is thus correctly treated as an update.\n */\n private async loadPersistedRow(): Promise<\n Record<string, unknown> | undefined\n > {\n if (!this.id) return undefined;\n try {\n const row = await this.db.get(this.tableName, { id: this.id });\n return row ?? undefined;\n } catch {\n // DB not ready / table absent — treat as new (in-memory fallback in save).\n return undefined;\n }\n }\n\n /**\n * Reject any change to the settled backing fields of an already-PAID/ISSUED\n * intent (codex HIGH#2). `paymentOptions` is compared structurally because\n * the winning option's `nativeAmount` is exactly what the reconciliation in\n * {@link assertBackedByCompletedPayment} binds against — letting it drift\n * would let a repointed `paymentId` match a doctored option.\n */\n private assertBackingFieldsUnchanged(\n priorRow: Record<string, unknown>,\n ): void {\n const frozen: Array<[string, unknown, unknown]> = [\n ['paymentId', priorRow.payment_id ?? '', this.paymentId],\n [\n 'paidOptionBackendId',\n priorRow.paid_option_backend_id ?? '',\n this.paidOptionBackendId,\n ],\n [\n 'usdPriceLocked',\n Number(priorRow.usd_price_locked ?? 0),\n Number(this.usdPriceLocked ?? 0),\n ],\n ];\n for (const [field, prior, next] of frozen) {\n if (prior !== next) {\n throw new Error(\n `PaymentIntent ${this.id}: '${field}' is frozen once the intent is ` +\n `PAID/ISSUED (was '${prior}', got '${next}'). A settled intent's ` +\n 'backing fields cannot be repointed — retire() it and issue a new intent.',\n );\n }\n }\n\n const priorOptions = PaymentIntent.normalizePaymentOptions(\n priorRow.payment_options as unknown,\n );\n const nextOptions = PaymentIntent.normalizePaymentOptions(\n this.paymentOptions as unknown,\n );\n if (JSON.stringify(priorOptions) !== JSON.stringify(nextOptions)) {\n throw new Error(\n `PaymentIntent ${this.id}: 'paymentOptions' is frozen once the intent ` +\n 'is PAID/ISSUED — the winning option backs the verified Payment ' +\n 'reconciliation and cannot change. retire() it and issue a new intent.',\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 re-saves (status unchanged) and brand-new rows are allowed (the\n * backing-Payment verification still runs separately for PAID/ISSUED).\n */\n private assertStatusTransition(prior: PaymentIntentStatus | undefined): void {\n if (prior === undefined) return; // new row\n if (prior === this.status) return; // no-op re-save\n const allowed = PAYMENT_INTENT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `PaymentIntent ${this.id}: illegal status transition '${prior}' → ` +\n `'${this.status}'. Use the guarded transition helpers ` +\n '(verifyAndMarkPaid / markIssued / expire / cancel / retire).',\n );\n }\n }\n\n /**\n * Verify the intent's `paidOptionBackendId` / `paymentId` reference a real,\n * COMPLETED, amount-matching Payment. Throws otherwise. Shared by\n * {@link verifyAndMarkPaid} (pre-transition) and the save-time guard\n * (catch-all for raw mass-assignment).\n */\n private async assertBackedByCompletedPayment(): Promise<void> {\n if (!this.paidOptionBackendId) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot persist PAID status without a paidOptionBackendId`,\n );\n }\n if (!this.paymentId) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot persist PAID status without a paymentId`,\n );\n }\n const option = this.getOption(this.paidOptionBackendId);\n if (!option) {\n throw new Error(\n `PaymentIntent ${this.id}: paidOptionBackendId '${this.paidOptionBackendId}' is not one of the listed options`,\n );\n }\n\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await PaymentCollection.create(this.options);\n const payment = await payments.get({ id: this.paymentId });\n if (!payment) {\n throw new Error(\n `PaymentIntent ${this.id}: referenced Payment '${this.paymentId}' does not exist`,\n );\n }\n if (payment.status !== PaymentStatus.COMPLETED) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${this.paymentId}' is not COMPLETED ` +\n `(status='${payment.status}'); cannot persist PAID intent against it`,\n );\n }\n this.reconcilePaymentWithOption(payment, option, this.paymentId);\n }\n\n /**\n * Reconcile a COMPLETED Payment against the winning {@link PaymentOption}\n * (S5 audit #1390). Beyond the amount check, the Payment must have arrived on\n * the SAME rail and currency the option quoted — otherwise an unrelated\n * completed payment that merely shares a numeric amount (e.g. a USD 199\n * payment) could satisfy a different option (e.g. a `base-usdc` 199 option),\n * marking the intent paid on the wrong rail with no matching funds. Compares\n * the Payment's `backendId` (rail) and native currency (falling back to its\n * settlement currency) against the option's `backendId` / `currency`, then\n * the native amount.\n *\n * Shared by {@link verifyAndMarkPaid} (pre-transition) and\n * {@link assertBackedByCompletedPayment} (save-time catch-all) so both gates\n * enforce the identical invariant.\n */\n private reconcilePaymentWithOption(\n payment: {\n backendId?: string;\n nativeCurrency?: string;\n currency?: string;\n nativeAmount?: number;\n amount?: number;\n },\n option: PaymentOption,\n paymentIdLabel: string,\n ): void {\n // Only enforce the rail check when the Payment actually recorded a backend\n // (PaymentBackend-routed flows). Manually-recorded payments may carry no\n // backendId; the amount + currency checks still apply.\n if (payment.backendId && payment.backendId !== option.backendId) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${paymentIdLabel}' arrived on rail ` +\n `'${payment.backendId}' but option '${option.backendId}' expects rail ` +\n `'${option.backendId}' — cannot mark paid on a mismatched rail`,\n );\n }\n // The option's `currency` is the native-rail-qualified code (e.g.\n // `USDC-base`), which lines up with the Payment's `nativeCurrency`. Fall\n // back to the settlement `currency` when no native currency was recorded.\n const paymentCurrency = payment.nativeCurrency || payment.currency || '';\n if (paymentCurrency && paymentCurrency !== option.currency) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${paymentIdLabel}' currency ` +\n `'${paymentCurrency}' does not match option '${option.backendId}' ` +\n `currency '${option.currency}'`,\n );\n }\n const paymentNative =\n typeof payment.nativeAmount === 'number' && payment.nativeAmount > 0\n ? payment.nativeAmount\n : payment.amount;\n if (\n typeof paymentNative !== 'number' ||\n Math.abs(paymentNative - option.nativeAmount) > PAYMENT_INTENT_EPSILON\n ) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${paymentIdLabel}' amount ${paymentNative} ` +\n `does not match option '${option.backendId}' nativeAmount ${option.nativeAmount}`,\n );\n }\n }\n\n // -------- Status helpers --------\n\n isAwaitingPayment(): boolean {\n return this.status === PaymentIntentStatus.AWAITING_PAYMENT;\n }\n\n isPaid(): boolean {\n return this.status === PaymentIntentStatus.PAID;\n }\n\n isIssued(): boolean {\n return this.status === PaymentIntentStatus.ISSUED;\n }\n\n isCancelled(): boolean {\n return this.status === PaymentIntentStatus.CANCELLED;\n }\n\n isRetired(): boolean {\n return this.status === PaymentIntentStatus.RETIRED;\n }\n\n /**\n * Terminal-state predicate: any status other than `AWAITING_PAYMENT`\n * is terminal (the intent will not accept further state changes\n * except the explicit `markIssued` after `PAID`).\n */\n isTerminal(): boolean {\n return this.status !== PaymentIntentStatus.AWAITING_PAYMENT;\n }\n\n /**\n * `Date.now()`-based predicate: has the price lock window passed?\n * Independent of `status` so callers can detect a stale-but-not-\n * yet-marked-EXPIRED intent (and call `expire()` to advance it).\n */\n isExpired(): boolean {\n if (this.status === PaymentIntentStatus.EXPIRED) return true;\n if (!this.priceLockExpiresAt) return false;\n return Date.now() > this.priceLockExpiresAt.getTime();\n }\n\n // -------- State transitions --------\n\n /**\n * Transition the intent to `PAID`, naming which option satisfied it\n * and which `Payment` row carries the funds. Implicitly retires the\n * other options — callers can check {@link isOptionRetired} to flag\n * later inbound funds for refund.\n *\n * Throws when called on a non-`AWAITING_PAYMENT` intent so a stale\n * caller can't accidentally overwrite a winning option with a\n * losing one.\n */\n markPaid(args: { backendId: string; paymentId: string }): void {\n if (this.status !== PaymentIntentStatus.AWAITING_PAYMENT) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot transition to PAID from status '${this.status}'`,\n );\n }\n // Refuse stale quotes. A backend can report a payment after the price-lock\n // window has passed but before a cleanup job has run `expire()` — the\n // intent is still AWAITING_PAYMENT, yet honoring it would accept funds at\n // an expired USD price. `isExpired()` is status-independent (it checks\n // `priceLockExpiresAt`), so guard on it here; callers should `expire()`\n // and re-quote rather than record against a lapsed lock.\n if (this.isExpired()) {\n throw new Error(\n `PaymentIntent ${this.id}: price lock expired at ` +\n `${this.priceLockExpiresAt?.toISOString() ?? 'unknown'}; cannot record a ` +\n `payment against a stale quote (call expire() and re-quote)`,\n );\n }\n if (!args.backendId) {\n throw new Error(\n `PaymentIntent ${this.id}: markPaid requires a backendId`,\n );\n }\n const option = this.paymentOptions.find(\n (o) => o.backendId === args.backendId,\n );\n if (!option) {\n throw new Error(\n `PaymentIntent ${this.id}: backendId '${args.backendId}' is not one of the listed options`,\n );\n }\n this.status = PaymentIntentStatus.PAID;\n this.paidOptionBackendId = args.backendId;\n this.paymentId = args.paymentId ?? '';\n this.paidAt = new Date();\n }\n\n /**\n * Verify-then-transition variant of {@link markPaid} (S5 audit #1390).\n *\n * `markPaid` trusts the caller-supplied `paymentId` / `backendId` without\n * checking the referenced `Payment` actually exists, is `COMPLETED`, or\n * carries the amount the winning option quoted. That lets a caller mark an\n * intent PAID against a non-existent or still-pending payment. This method\n * loads the `Payment` row and enforces:\n *\n * - the Payment exists,\n * - it is `COMPLETED` (not pending / failed / cancelled / refunded),\n * - it arrived on the same rail (`backendId`) and currency the option\n * quoted (so an unrelated completed payment that merely shares a numeric\n * amount can't satisfy a different option),\n * - and its amount reconciles with the winning option's `nativeAmount`\n * (within sub-cent tolerance), falling back to the option vs. the\n * Payment's settlement `amount` when no native rail is recorded.\n *\n * Only after those pass does it delegate to the sync `markPaid` for the\n * status-machine invariants.\n *\n * @param args.backendId backendId of the option that was satisfied\n * @param args.paymentId id of the Payment row carrying the funds (required)\n */\n async verifyAndMarkPaid(args: {\n backendId: string;\n paymentId: string;\n }): Promise<void> {\n if (!args.paymentId) {\n throw new Error(\n `PaymentIntent ${this.id}: verifyAndMarkPaid requires a paymentId`,\n );\n }\n const option = this.paymentOptions.find(\n (o) => o.backendId === args.backendId,\n );\n if (!option) {\n throw new Error(\n `PaymentIntent ${this.id}: backendId '${args.backendId}' is not one of the listed options`,\n );\n }\n\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await PaymentCollection.create(this.options);\n const payment = await payments.get({ id: args.paymentId });\n if (!payment) {\n throw new Error(\n `PaymentIntent ${this.id}: referenced Payment '${args.paymentId}' does not exist`,\n );\n }\n if (payment.status !== PaymentStatus.COMPLETED) {\n throw new Error(\n `PaymentIntent ${this.id}: Payment '${args.paymentId}' is not COMPLETED ` +\n `(status='${payment.status}'); cannot mark intent PAID against it`,\n );\n }\n\n // Reconcile the payment against the winning option — same rail, currency,\n // and amount (shared with the save-time guard so both gates agree).\n this.reconcilePaymentWithOption(payment, option, args.paymentId);\n\n this.markPaid({ backendId: args.backendId, paymentId: args.paymentId });\n }\n\n /**\n * Transition a `PAID` intent to `ISSUED` once the downstream rights\n * / contract / fulfillment have been created. Idempotent — calling\n * twice is a no-op so callers don't have to guard against retries.\n */\n markIssued(): void {\n if (this.status === PaymentIntentStatus.ISSUED) return;\n if (this.status !== PaymentIntentStatus.PAID) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot transition to ISSUED from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.ISSUED;\n this.issuedAt = new Date();\n }\n\n /**\n * Move an `AWAITING_PAYMENT` intent to `EXPIRED`. Safe to call on\n * an already-expired intent (no-op). Throws on terminal non-\n * expired states so a confused caller can't undo a paid / issued\n * intent.\n */\n expire(): void {\n if (this.status === PaymentIntentStatus.EXPIRED) return;\n if (this.status !== PaymentIntentStatus.AWAITING_PAYMENT) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot expire from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.EXPIRED;\n this.expiredAt = new Date();\n }\n\n /**\n * Cancel an open intent. Only valid from `AWAITING_PAYMENT` —\n * a paid / issued intent is no longer the buyer's to cancel; that\n * path is a refund, modelled separately on `Payment`.\n */\n cancel(reason?: string): void {\n if (this.status !== PaymentIntentStatus.AWAITING_PAYMENT) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot cancel from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.CANCELLED;\n this.cancelledAt = new Date();\n if (reason) {\n this.notes = this.notes\n ? `${this.notes}\\nCancelled: ${reason}`\n : `Cancelled: ${reason}`;\n }\n }\n\n /**\n * Mark a paid intent as retired — used when a previously-recorded\n * payment was reversed (chargeback, refund) and the intent should\n * not be considered \"satisfied\" anymore. Distinct from `cancel()`\n * which only applies to never-paid intents.\n */\n retire(reason?: string): void {\n if (\n this.status !== PaymentIntentStatus.PAID &&\n this.status !== PaymentIntentStatus.ISSUED\n ) {\n throw new Error(\n `PaymentIntent ${this.id}: cannot retire from status '${this.status}'`,\n );\n }\n this.status = PaymentIntentStatus.RETIRED;\n this.retiredAt = new Date();\n if (reason) {\n this.notes = this.notes\n ? `${this.notes}\\nRetired: ${reason}`\n : `Retired: ${reason}`;\n }\n }\n\n // -------- Option helpers --------\n\n /**\n * Return the option whose `backendId` matches, or `undefined` if\n * no such option is on this intent. Useful for routing inbound\n * payments to the right option's `nativeAmount` / `payTo` for\n * verification.\n */\n getOption(backendId: string): PaymentOption | undefined {\n return this.paymentOptions.find((o) => o.backendId === backendId);\n }\n\n /**\n * `true` once the intent is paid and the given option was NOT the\n * winning one. Consumers use this to flag inbound funds to retired\n * options for refund. Returns `false` for the winning option, for\n * unpaid intents, and for an unknown `backendId`.\n */\n isOptionRetired(backendId: string): boolean {\n if (!this.isPaid() && !this.isIssued() && !this.isRetired()) return false;\n if (!this.paidOptionBackendId) return false;\n if (!this.getOption(backendId)) return false;\n return backendId !== this.paidOptionBackendId;\n }\n\n /**\n * The options that were NOT selected at payment time. Returns an\n * empty array for unpaid intents so callers can iterate without\n * special-casing.\n */\n getRetiredOptions(): PaymentOption[] {\n if (!this.paidOptionBackendId) return [];\n return this.paymentOptions.filter(\n (o) => o.backendId !== this.paidOptionBackendId,\n );\n }\n\n // -------- Normalization helpers --------\n\n /**\n * Defensive coercion for `paymentOptions` input. Accepts either an\n * already-parsed array or a JSON string (e.g. from a row whose\n * column was hand-edited or migrated from a different schema).\n * Drops entries that don't carry the required `backendId` /\n * `currency` / `payTo` / `nativeAmount` quartet so downstream\n * routing code can rely on the invariant.\n */\n private static normalizePaymentOptions(value: unknown): PaymentOption[] {\n if (value == null) return [];\n let parsed: unknown = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(parsed)) return [];\n const out: PaymentOption[] = [];\n for (const raw of parsed) {\n if (!raw || typeof raw !== 'object') continue;\n const o = raw as Record<string, unknown>;\n const backendId = typeof o.backendId === 'string' ? o.backendId : '';\n const currency = typeof o.currency === 'string' ? o.currency : '';\n const payTo = typeof o.payTo === 'string' ? o.payTo : '';\n const nativeAmount =\n typeof o.nativeAmount === 'number' ? o.nativeAmount : Number.NaN;\n if (!backendId || !currency || !payTo || !Number.isFinite(nativeAmount)) {\n continue;\n }\n const option: PaymentOption = {\n backendId,\n currency,\n payTo,\n nativeAmount,\n };\n if (typeof o.chain === 'string' && o.chain) option.chain = o.chain;\n if (typeof o.memo === 'string' && o.memo) option.memo = o.memo;\n if (typeof o.x402Capable === 'boolean') {\n option.x402Capable = o.x402Capable;\n }\n if (typeof o.expiresAt === 'string' && o.expiresAt) {\n option.expiresAt = o.expiresAt;\n }\n out.push(option);\n }\n return out;\n }\n\n /**\n * Coerce a Date-ish input (Date / number / ISO string / null /\n * undefined) into a `Date | null`. The framework hands us strings\n * when hydrating from SQLite, numbers when round-tripping through\n * JSON, and Date instances from the application.\n */\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default PaymentIntent;\n","/**\n * PaymentIntentCollection — collection manager for {@link PaymentIntent}.\n *\n * Adds helpers for the common query shapes (find by idempotency key,\n * find still-open intents, find by licensee email, find expired) and a\n * `getOrCreateByIdempotencyKey` helper that callers use to implement\n * safe at-least-once retries from the marketplace's PaymentIntent\n * issuance handler.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { PaymentIntent } from '../models/PaymentIntent.js';\nimport { PaymentIntentStatus, type PaymentOption } from '../types/index.js';\n\nexport interface PaymentIntentIdempotencyArgs {\n /** Caller-supplied scope (Sku id / tier id / etc.) */\n offeringRef: string;\n /** Buyer email — second factor of the natural key */\n licenseeEmail: string;\n /** Caller-supplied idempotency key */\n idempotencyKey: string;\n /** Optional tenant scope, when called outside an active tenant context */\n tenantId?: string | null;\n}\n\nexport interface PaymentIntentSeed\n extends Partial<Omit<PaymentIntentIdempotencyArgs, 'tenantId'>> {\n tenantId?: string | null;\n skuId?: string;\n customerId?: string;\n paymentOptions: PaymentOption[];\n usdPriceLocked: number;\n priceLockWindowMs?: number;\n}\n\nexport class PaymentIntentCollection extends SmrtCollection<PaymentIntent> {\n static readonly _itemClass = PaymentIntent;\n\n /**\n * Look up an existing intent by its natural key. Returns `null` when\n * any of the three components are empty — empty natural-key inputs\n * disable dedup by design, so a `null` return tells the caller to\n * fall through to a fresh `create`.\n */\n async findByIdempotencyKey(\n args: PaymentIntentIdempotencyArgs,\n ): Promise<PaymentIntent | null> {\n if (!args.offeringRef || !args.licenseeEmail || !args.idempotencyKey) {\n return null;\n }\n const where: Record<string, unknown> = {\n offeringRef: args.offeringRef,\n licenseeEmail: args.licenseeEmail,\n idempotencyKey: args.idempotencyKey,\n };\n if (args.tenantId !== undefined) {\n where.tenantId = args.tenantId;\n }\n const results = await this.list({ where, limit: 1 });\n return results[0] ?? null;\n }\n\n /**\n * Idempotency-aware create. If an intent already exists with the\n * same `(tenantId, offeringRef, licenseeEmail, idempotencyKey)`\n * tuple, returns the existing row. Otherwise creates a new one\n * with the supplied seed.\n *\n * Returns a `{ intent, created }` pair so callers can branch on\n * whether they're handling a fresh quote or a replay (e.g. to\n * skip a duplicate \"intent created\" webhook emission).\n */\n async getOrCreateByIdempotencyKey(\n seed: PaymentIntentSeed,\n ): Promise<{ intent: PaymentIntent; created: boolean }> {\n const existing = await this.findByIdempotencyKey({\n offeringRef: seed.offeringRef ?? '',\n licenseeEmail: seed.licenseeEmail ?? '',\n idempotencyKey: seed.idempotencyKey ?? '',\n tenantId: seed.tenantId,\n });\n if (existing) {\n return { intent: existing, created: false };\n }\n return { intent: await this.create({ ...seed }), created: true };\n }\n\n /**\n * Open intents — `AWAITING_PAYMENT` and not yet past the price-\n * lock window. The price-lock check is `Date.now()`-based, so a\n * stale-but-not-yet-marked-EXPIRED row drops out of this list even\n * before a cleanup job advances its status.\n */\n async findOpen(): Promise<PaymentIntent[]> {\n const open = await this.list({\n where: { status: PaymentIntentStatus.AWAITING_PAYMENT },\n orderBy: 'created_at DESC',\n });\n const now = Date.now();\n return open.filter((p) => {\n if (!p.priceLockExpiresAt) return true;\n return p.priceLockExpiresAt.getTime() > now;\n });\n }\n\n /**\n * `AWAITING_PAYMENT` intents whose price-lock window has already\n * passed. The expected use is a periodic cleanup job that loads\n * this list, calls `intent.expire(); await intent.save()` on each,\n * and emits whatever downstream signal the application needs.\n */\n async findStale(): Promise<PaymentIntent[]> {\n const open = await this.list({\n where: { status: PaymentIntentStatus.AWAITING_PAYMENT },\n });\n const now = Date.now();\n return open.filter((p) => {\n if (!p.priceLockExpiresAt) return false;\n return p.priceLockExpiresAt.getTime() <= now;\n });\n }\n\n async findByLicenseeEmail(email: string): Promise<PaymentIntent[]> {\n return this.list({\n where: { licenseeEmail: email },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByOfferingRef(offeringRef: string): Promise<PaymentIntent[]> {\n return this.list({\n where: { offeringRef },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByStatus(status: PaymentIntentStatus): Promise<PaymentIntent[]> {\n return this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n async findByTenant(tenantId: string): Promise<PaymentIntent[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payment intents (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 async findGlobal(): Promise<PaymentIntent[]> {\n return queryGlobal<PaymentIntent>(this);\n }\n\n /**\n * Find payment intents for a tenant including global intents.\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 async findWithGlobals(tenantId: string): Promise<PaymentIntent[]> {\n return queryWithGlobals<PaymentIntent>(\n this,\n tenantId,\n 'PaymentIntent.findWithGlobals',\n );\n }\n}\n","/**\n * Payout — operator-to-supplier outgoing remittance.\n *\n * A Payout sits on the opposite side of a {@link Payment}: it tracks\n * funds *leaving* the operator's wallet and arriving at a vendor's\n * payout address. The two are kept as distinct models (rather than\n * overloading `Payment` with a `direction` field) because the status\n * machine, chain semantics, and audit story differ.\n *\n * Industry-neutral: any operator that ingests one payment and remits\n * funds to a third party — marketplaces, payment processors with\n * Connect-style fan-out, royalty distributors, escrow services —\n * benefits.\n *\n * MVP scope: no currency conversion. The payout's amounts and currency\n * mirror the originating Payment's native currency, and the vendor\n * needs a matching `Vendor.payoutAddresses` entry to receive funds.\n * Filtering at quote time (`PaymentIntent.paymentOptions`) is the\n * consumer's job; the model just enforces the natural-key invariant\n * and the status machine.\n *\n * @packageDocumentation\n */\n\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { type PayoutOptions, PayoutStatus } from '../types/index.js';\nimport { Vendor } from './Vendor.js';\n\n/**\n * Sub-cent rounding tolerance, matching the rest of the package's EPSILON\n * convention.\n */\nconst PAYOUT_EPSILON = 0.01;\n\n/**\n * Legal status transitions for a Payout, keyed by the prior persisted status.\n * Mirrors the state machine enforced by `markSent` / `markConfirmed` /\n * `markFailed` / `resetFromFailed`, so a raw `status` mass-assignment on the\n * generated update route can't skip steps (e.g. `pending → confirmed`) or\n * revive a terminal-ish state outside the dedicated reset path.\n *\n * `pending → sent → confirmed`, with `failed` reachable from `pending` / `sent`\n * and `failed → pending` allowed only via `resetFromFailed()`.\n */\nconst PAYOUT_STATUS_TRANSITIONS: Record<PayoutStatus, PayoutStatus[]> = {\n [PayoutStatus.PENDING]: [PayoutStatus.SENT, PayoutStatus.FAILED],\n [PayoutStatus.SENT]: [PayoutStatus.CONFIRMED, PayoutStatus.FAILED],\n [PayoutStatus.CONFIRMED]: [],\n // FAILED is resettable to PENDING via resetFromFailed().\n [PayoutStatus.FAILED]: [PayoutStatus.PENDING],\n};\n\n/**\n * Module-scoped record of the status each Payout instance was loaded with, so\n * the save-time transition guard can compare the prior persisted status\n * against the one being written. WeakMap keeps it out of the schema and GCs\n * with the instance — same pattern as the other commerce models.\n */\nconst loadedPayoutStatus = new WeakMap<Payout, PayoutStatus>();\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // ROOT FIX (S5 audit #1390 round 4): a Payout has NO safe generated write.\n // Its status drives an outgoing remittance and its amount triple\n // (grossAmount / operatorFee / supplierNet) is the integrity core — none may\n // be set by a caller. The only legitimate way to mint a payout is the\n // verified `PayoutCollection.createFromPayment()` domain path (which derives\n // the amounts from the source Payment and starts PENDING), and the only legal\n // status moves are the guarded `markSent` / `markConfirmed` / `markFailed` /\n // `resetFromFailed` helpers. So the generated surface is read-only — this\n // closes the \"forged CONFIRMED Payout via api.create\" vector (codex HIGH#4)\n // at the surface, not just in the save() guard.\n //\n // CLI parity (round 6, codex ROOT INSIGHT): the CLI is an independently\n // configured write surface. `cli: true` would still generate create/update/\n // delete commands that set status / the gross-fee-net triple, re-opening the\n // exact vector closed on api/mcp. The CLI is locked to the same read-only\n // surface — there is no safe generated payout write on ANY surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: { include: ['list', 'get'] },\n})\nexport class Payout extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation. Nullable so global / single-\n * tenant deployments work too.\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * The source {@link Payment} that funded this payout. Plain string\n * reference (FK convention: `@foreignKey('ModelName')` would be\n * fine within the package, but `Payment` lives in this same\n * package — staying with a string keeps the dependency graph\n * clean and matches how `PaymentIntent` references `Payment`).\n */\n paymentId: string = '';\n\n /**\n * The destination {@link Vendor}. Foreign-key constrained because\n * Vendor lives in this same package and a hard reference catches\n * dangling payouts at insert time.\n */\n @foreignKey(Vendor)\n vendorId: string = '';\n\n /**\n * Total funds moved, in the originating payment's native currency.\n * Stored at decimal precision (matches `Payment.amount` /\n * `Payment.nativeAmount` convention).\n */\n grossAmount: number = 0.0;\n\n /**\n * Operator's take from `grossAmount`. Must equal\n * `grossAmount - supplierNet` at save time — the model enforces\n * the invariant via `validateAmounts()`.\n */\n operatorFee: number = 0.0;\n\n /**\n * Net funds the supplier receives. Must equal\n * `grossAmount - operatorFee` at save time.\n */\n supplierNet: number = 0.0;\n\n /**\n * Native currency the funds are denominated in — payout-rail-\n * qualified, matching `Payment.nativeCurrency` and\n * `Vendor.payoutAddresses` keys (`USDC-base`, `BTC`, `USD-stripe`,\n * etc.).\n */\n currency: string = '';\n\n /**\n * The `PaymentBackend` adapter id used to send the payout. For a\n * marketplace this typically matches the source `Payment.backendId`\n * (currency-match settlement, no conversion); other consumers can\n * use a different backend if they bridge currencies elsewhere.\n */\n backendId: string = '';\n\n /**\n * Outgoing chain transaction hash, gateway settlement id, or\n * (for not-yet-broadcast BTC payouts) a PSBT identifier. Set when\n * the payout transitions to `SENT`. Empty until then.\n */\n backendTxRef: string = '';\n\n /**\n * Current status — see {@link PayoutStatus} for the state machine.\n * Mutate via `markSent` / `markConfirmed` / `markFailed` /\n * `resetFromFailed` rather than direct assignment so the\n * transition invariants run.\n */\n status: PayoutStatus = PayoutStatus.PENDING;\n\n /**\n * When the payout transitioned to `SENT`.\n */\n sentAt: Date | null = null;\n\n /**\n * When the payout transitioned to `CONFIRMED`.\n */\n confirmedAt: Date | null = null;\n\n /**\n * When the payout transitioned to `FAILED`.\n */\n failedAt: Date | null = null;\n\n /**\n * Caller-supplied human-readable failure reason. Empty for non-\n * failed payouts. Cleared by `resetFromFailed()`.\n */\n failureReason: string = '';\n\n /**\n * Append-only operator notes — chargeback memos, manual-retry\n * justifications, etc.\n */\n notes: string = '';\n\n constructor(options: PayoutOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.paymentId !== undefined) this.paymentId = options.paymentId;\n if (options.vendorId !== undefined) this.vendorId = options.vendorId;\n if (options.grossAmount !== undefined)\n this.grossAmount = options.grossAmount;\n if (options.operatorFee !== undefined)\n this.operatorFee = options.operatorFee;\n if (options.supplierNet !== undefined)\n this.supplierNet = options.supplierNet;\n if (options.currency !== undefined) this.currency = options.currency;\n if (options.backendId !== undefined) this.backendId = options.backendId;\n if (options.backendTxRef !== undefined)\n this.backendTxRef = options.backendTxRef;\n if (options.status !== undefined) this.status = options.status;\n if (options.sentAt !== undefined)\n this.sentAt = Payout.coerceDate(options.sentAt);\n if (options.confirmedAt !== undefined)\n this.confirmedAt = Payout.coerceDate(options.confirmedAt);\n if (options.failedAt !== undefined)\n this.failedAt = Payout.coerceDate(options.failedAt);\n if (options.failureReason !== undefined)\n this.failureReason = options.failureReason;\n if (options.notes !== undefined) this.notes = options.notes;\n }\n\n /**\n * Re-coerce timestamp fields after the framework reapplies raw option\n * values during initialization.\n */\n override async initialize(): Promise<this> {\n await super.initialize();\n this.sentAt = Payout.coerceDate(this.sentAt);\n this.confirmedAt = Payout.coerceDate(this.confirmedAt);\n this.failedAt = Payout.coerceDate(this.failedAt);\n if (await this.isSaved()) {\n loadedPayoutStatus.set(this, this.status);\n }\n return this;\n }\n\n // -------- Status predicates --------\n\n isPending(): boolean {\n return this.status === PayoutStatus.PENDING;\n }\n\n isSent(): boolean {\n return this.status === PayoutStatus.SENT;\n }\n\n isConfirmed(): boolean {\n return this.status === PayoutStatus.CONFIRMED;\n }\n\n isFailed(): boolean {\n return this.status === PayoutStatus.FAILED;\n }\n\n /**\n * Transition `PENDING → SENT`. Requires `backendTxRef` — without a\n * way to identify the outgoing transaction the payout cannot be\n * tracked further. Throws on any other source status so a confused\n * caller can't accidentally roll back a confirmed payout.\n */\n markSent(backendTxRef: string): void {\n if (this.status !== PayoutStatus.PENDING) {\n throw new Error(\n `Payout ${this.id}: cannot transition to SENT from status '${this.status}'`,\n );\n }\n if (!backendTxRef) {\n throw new Error(`Payout ${this.id}: markSent requires a backendTxRef`);\n }\n this.backendTxRef = backendTxRef;\n this.status = PayoutStatus.SENT;\n this.sentAt = new Date();\n }\n\n /**\n * Transition `SENT → CONFIRMED`. Throws if called from any other\n * status — confirmations on a pending payout would mean a chain\n * confirmation arrived before the application even recorded the\n * outgoing tx, which is a code bug worth surfacing rather than\n * silently fixing.\n */\n markConfirmed(): void {\n if (this.status !== PayoutStatus.SENT) {\n throw new Error(\n `Payout ${this.id}: cannot transition to CONFIRMED from status '${this.status}'`,\n );\n }\n this.status = PayoutStatus.CONFIRMED;\n this.confirmedAt = new Date();\n }\n\n /**\n * Move the payout to `FAILED`. Valid from `PENDING` (couldn't even\n * broadcast) or `SENT` (broadcast but chain rejected). Confirmed\n * payouts cannot fail — that path is a refund, modelled separately.\n */\n markFailed(reason: string): void {\n if (\n this.status !== PayoutStatus.PENDING &&\n this.status !== PayoutStatus.SENT\n ) {\n throw new Error(\n `Payout ${this.id}: cannot transition to FAILED from status '${this.status}'`,\n );\n }\n this.status = PayoutStatus.FAILED;\n this.failedAt = new Date();\n this.failureReason = reason ?? '';\n }\n\n /**\n * Operator-driven reset: move a `FAILED` payout back to `PENDING`\n * after fixing whatever broke. Clears the failure metadata and the\n * old `backendTxRef` (the next attempt will have a new one).\n * Distinct from `markFailed` reflexivity — the issue spec says\n * \"failed is terminal but allows manual reset via explicit method\".\n */\n resetFromFailed(): void {\n if (this.status !== PayoutStatus.FAILED) {\n throw new Error(\n `Payout ${this.id}: cannot reset from status '${this.status}' — only FAILED is resettable`,\n );\n }\n this.status = PayoutStatus.PENDING;\n this.sentAt = null;\n this.confirmedAt = null;\n this.failedAt = null;\n this.failureReason = '';\n // Drop the old tx ref so the next markSent attempt gets a fresh one.\n this.backendTxRef = '';\n }\n\n /**\n * Throws if the gross/fee/net invariant doesn't hold. Tolerates a\n * sub-cent rounding fuzz (`EPSILON = 0.01`) to match the rest of\n * the smrt-ledgers package's tolerance.\n */\n validateAmounts(): void {\n const EPSILON = 0.01;\n // Non-negativity guard (S5 audit #1390). A negative operatorFee would let\n // an operator manufacture a supplierNet larger than the gross it took in\n // (paying out more than it received); negative gross/net are nonsensical.\n for (const [field, value] of [\n ['grossAmount', this.grossAmount],\n ['operatorFee', this.operatorFee],\n ['supplierNet', this.supplierNet],\n ] as const) {\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: ${field} must be a non-negative number (got ${value}).`,\n );\n }\n }\n const expectedNet = this.grossAmount - this.operatorFee;\n if (Math.abs(this.supplierNet - expectedNet) > EPSILON) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: amount invariant violated — ` +\n `gross=${this.grossAmount} fee=${this.operatorFee} net=${this.supplierNet} ` +\n `(expected net=${expectedNet})`,\n );\n }\n }\n\n /**\n * Save-time state-machine + settlement guard (S5 audit #1390 round 2).\n *\n * `status`, `backendTxRef`, and the amount triple are all mass-assignable on\n * the generated create/update routes. The amount invariant alone (round 1)\n * still let a caller raw-flip a persisted payout to `CONFIRMED` / `SENT`\n * (skipping the chain steps) or remit a `grossAmount` larger than the source\n * Payment actually brought in — money that never arrived.\n *\n * This guard adds:\n * - **Transitions on an existing row must be legal** per\n * {@link PAYOUT_STATUS_TRANSITIONS} (no `pending → confirmed` skip on a\n * raw update, no reviving a CONFIRMED payout). Brand-new rows may be\n * created in any status (collection/import/query fixtures), but advancing\n * a persisted row is constrained to the legal edges the helpers enforce.\n * - **SENT requires a backendTxRef** (matches `markSent`'s invariant) so a\n * sent payout is always traceable, regardless of how the status was set.\n * - **grossAmount is capped by the source Payment** when that Payment\n * resolves: a payout can never remit more than the funding Payment\n * settled. A `paymentId` that doesn't resolve (synthetic id, Payment-less\n * deployment) skips the cap — the source Payment is a plain-string,\n * cross-model reference by design and may legitimately be a manually\n * recorded (non-COMPLETED) row, so the cap is best-effort rather than a\n * hard existence requirement.\n *\n * The amount-invariant check (`validateAmounts`) still runs first as the\n * arithmetic floor.\n */\n override async save(): Promise<this> {\n this.validateAmounts();\n\n const prior = await this.resolvePriorStatus();\n this.assertStatusTransition(prior);\n\n // SENT *and* CONFIRMED both require a backendTxRef (S5 audit #1390 round 4):\n // a confirmed payout that carries no outgoing-transaction reference is\n // untraceable and indistinguishable from a forged confirmation. Round 3\n // only required it for SENT, leaving a raw CONFIRMED-with-no-txref hole.\n if (\n (this.status === PayoutStatus.SENT ||\n this.status === PayoutStatus.CONFIRMED) &&\n !this.backendTxRef\n ) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: a ${this.status} payout requires a ` +\n 'backendTxRef (use markSent()).',\n );\n }\n\n // CONFIRMED is only ever reachable from SENT (codex HIGH#4 / HIGH#3): an\n // existing row's transition is checked by assertStatusTransition, but a\n // brand-new row (no prior) would otherwise be allowed to start CONFIRMED.\n // A confirmation means the chain/gateway acknowledged a previously-sent tx,\n // so it can never be the genesis state.\n if (this.status === PayoutStatus.CONFIRMED && prior !== PayoutStatus.SENT) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: CONFIRMED is only reachable from SENT ` +\n '(use markConfirmed() after markSent()).',\n );\n }\n\n await this.assertCappedBySourcePayment();\n\n const result = (await super.save()) as this;\n loadedPayoutStatus.set(this, this.status);\n return result;\n }\n\n /**\n * Resolve the AUTHORITATIVE prior status from the database (S5 audit #1390\n * round 4). The {@link loadedPayoutStatus} WeakMap is only populated when\n * {@link initialize} hydrated the row, so a `create({ id: <existing>,\n * _skipLoad: true })` upsert produces an instance with a missing WeakMap\n * entry — trusting it would treat the write as a brand-new row and skip the\n * transition + CONFIRMED-genesis guards. Reading the persisted row directly\n * makes a create-onto-existing behave as an update. `undefined` means no row\n * exists (genuinely new).\n */\n private async resolvePriorStatus(): Promise<PayoutStatus | undefined> {\n if (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 PayoutStatus;\n }\n } catch {\n // DB not ready — fall through to the in-memory record.\n }\n }\n return loadedPayoutStatus.get(this);\n }\n\n /**\n * Reject an illegal status flip on an existing row. Brand-new payouts (no\n * prior) may start in any status — collection imports and query fixtures\n * legitimately seed SENT / CONFIRMED rows — but advancing a *persisted* row\n * is constrained to the legal edges the helpers enforce, so a raw update\n * can't skip `pending → confirmed` or revive a terminal CONFIRMED.\n */\n private assertStatusTransition(prior: PayoutStatus | undefined): void {\n if (prior === undefined) return; // new row\n if (prior === this.status) return; // no-op re-save\n const allowed = PAYOUT_STATUS_TRANSITIONS[prior] ?? [];\n if (!allowed.includes(this.status)) {\n throw new Error(\n `Payout ${this.id}: illegal status transition '${prior}' → ` +\n `'${this.status}'. Use the guarded helpers (markSent / markConfirmed ` +\n '/ markFailed / resetFromFailed).',\n );\n }\n }\n\n /**\n * Cap `grossAmount` by the source {@link Payment}'s settled funds. A payout\n * that remits more than the Payment that funded it brought in is money the\n * operator never received.\n *\n * HARD requirement (S5 audit #1390 round 4, codex HIGH#3): the source Payment\n * MUST resolve. Round 3 made the cap best-effort — a `paymentId` that didn't\n * resolve silently skipped the cap, so a caller could remit an arbitrary\n * `grossAmount` simply by pointing at a non-existent (or empty) payment.\n * A payout's whole purpose is to remit funds that *arrived* via a Payment;\n * without a resolvable source there is no funded amount to cap against, so we\n * reject rather than skip. `PayoutCollection.createFromPayment()` always wires\n * a real `paymentId`, so this never blocks the supported creation path.\n */\n private async assertCappedBySourcePayment(): Promise<void> {\n if (!this.paymentId) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: a paymentId is required — a payout must ` +\n 'reference the source Payment that funded it (use createFromPayment()).',\n );\n }\n\n const { PaymentCollection } = await import(\n '../collections/PaymentCollection.js'\n );\n const payments = await PaymentCollection.create(this.options);\n const payment = await payments.get({ id: this.paymentId });\n if (!payment) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: referenced source Payment ` +\n `'${this.paymentId}' does not exist — refusing to remit against a ` +\n 'missing payment (the gross-amount cap cannot be enforced otherwise).',\n );\n }\n\n // Cap against the payment's funds. Prefer the native-rail figure when\n // present (volatile-currency rails), otherwise the settlement amount —\n // mirrors the PaymentIntent reconciliation convention.\n const settled =\n typeof payment.nativeAmount === 'number' && payment.nativeAmount > 0\n ? payment.nativeAmount\n : payment.amount;\n if (this.grossAmount - settled > PAYOUT_EPSILON) {\n throw new Error(\n `Payout ${this.id ?? '<new>'}: grossAmount ${this.grossAmount} exceeds the ` +\n `source Payment '${this.paymentId}' funded amount ${settled}.`,\n );\n }\n }\n\n private static coerceDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n}\n\nexport default Payout;\n","/**\n * PayoutCollection — collection manager for {@link Payout} rows.\n *\n * Adds helpers for the common operator-side query shapes: payouts for\n * a specific vendor, payouts derived from a specific source payment,\n * status-bucketed lookups for the operator dashboard, and a\n * `createFromPayment` convenience that wires up the source-payment\n * link in one call.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport type { Payment } from '../models/Payment.js';\nimport { Payout } from '../models/Payout.js';\nimport { PayoutStatus } from '../types/index.js';\n\nexport interface CreatePayoutFromPaymentArgs {\n /** Source Payment row */\n payment: Payment;\n /** Destination vendor id */\n vendorId: string;\n /** Operator fee in the payment's native currency */\n operatorFee: number;\n /** Backend that will send the payout — defaults to the payment's */\n backendId?: string;\n /** Optional initial notes */\n notes?: string;\n}\n\nexport class PayoutCollection extends SmrtCollection<Payout> {\n static readonly _itemClass = Payout;\n\n /**\n * Build a `PENDING` payout linked to a source payment. The gross\n * defaults to the payment's `nativeAmount` (falling back to\n * `amount` if no backend rail was recorded), the currency defaults\n * to the payment's `nativeCurrency` (falling back to `currency`),\n * and `supplierNet` is computed as `gross - operatorFee`.\n *\n * The returned instance is initialized but not persisted. The caller\n * is still responsible for `await payout.save()` — keeping create /\n * save as separate steps lets callers stitch the payout into an\n * application-level transaction.\n */\n async createFromPayment(args: CreatePayoutFromPaymentArgs): Promise<Payout> {\n const { payment, vendorId, operatorFee } = args;\n const grossAmount = payment.nativeAmount || payment.amount || 0;\n const currency = payment.nativeCurrency || payment.currency || '';\n const backendId = args.backendId ?? payment.backendId ?? '';\n const payout = new Payout({\n ai: this.options.ai,\n db: this.db,\n _skipLoad: true,\n // Inherit the source payment's tenant. Payout is @TenantScoped, so when\n // this runs outside an active tenant context (e.g. a background payout\n // job iterating Payment rows), the interceptor's auto-populate can't\n // stamp a tenant — without carrying it explicitly the remittance would\n // save as global/null and drop out of tenant-filtered payout queries.\n tenantId: payment.tenantId,\n paymentId: payment.id ?? '',\n vendorId,\n grossAmount,\n operatorFee,\n supplierNet: grossAmount - operatorFee,\n currency,\n backendId,\n status: PayoutStatus.PENDING,\n notes: args.notes ?? '',\n });\n await payout.initialize();\n return payout;\n }\n\n async findByPayment(paymentId: string): Promise<Payout[]> {\n return this.list({\n where: { paymentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByVendor(vendorId: string): Promise<Payout[]> {\n return this.list({\n where: { vendorId },\n orderBy: 'created_at DESC',\n });\n }\n\n async findByStatus(status: PayoutStatus): Promise<Payout[]> {\n return this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n async findPending(): Promise<Payout[]> {\n return this.findByStatus(PayoutStatus.PENDING);\n }\n\n async findFailed(): Promise<Payout[]> {\n return this.findByStatus(PayoutStatus.FAILED);\n }\n\n async findByBackendTxRef(backendTxRef: string): Promise<Payout | null> {\n if (!backendTxRef) return null;\n const results = await this.list({\n where: { backendTxRef },\n limit: 1,\n });\n return results[0] ?? null;\n }\n\n /**\n * Total funds delivered to a vendor in confirmed payouts. Useful for\n * a vendor-balance widget on the operator dashboard.\n */\n async getConfirmedTotalForVendor(vendorId: string): Promise<number> {\n const confirmed = await this.list({\n where: { vendorId, status: PayoutStatus.CONFIRMED },\n });\n return confirmed.reduce((sum, p) => sum + p.supplierNet, 0);\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n async findByTenant(tenantId: string): Promise<Payout[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global payouts (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 async findGlobal(): Promise<Payout[]> {\n return queryGlobal<Payout>(this);\n }\n\n /**\n * Find payouts for a tenant including global payouts.\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 async findWithGlobals(tenantId: string): Promise<Payout[]> {\n return queryWithGlobals<Payout>(this, tenantId, 'Payout.findWithGlobals');\n }\n}\n","/**\n * VendorCollection - Collection manager for Vendor objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Vendor } from '../models/Vendor.js';\nimport { VendorStatus } from '../types/index.js';\n\nexport class VendorCollection extends SmrtCollection<Vendor> {\n static readonly _itemClass = Vendor;\n\n /**\n * Find vendors by profile ID\n *\n * @param profileId - Profile ID from smrt-profiles\n * @returns Array of vendors linked to this profile\n */\n async findByProfile(profileId: string): Promise<Vendor[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active vendors\n *\n * @returns Array of active vendors\n */\n async findActive(): Promise<Vendor[]> {\n return await this.list({\n where: { status: VendorStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find vendors by status\n *\n * @param status - Vendor status\n * @returns Array of vendors\n */\n async findByStatus(status: VendorStatus): Promise<Vendor[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Get or create a vendor for a profile\n *\n * @param profileId - Profile ID\n * @param defaults - Default values if creating\n * @returns Vendor\n */\n async getOrCreateForProfile(\n profileId: string,\n defaults: Partial<{\n leadTimeDays: number;\n minimumOrderAmount: number;\n paymentTerms: string;\n }> = {},\n ): Promise<Vendor> {\n const existing = await this.findByProfile(profileId);\n if (existing.length > 0) {\n return existing[0];\n }\n\n const vendor = await this.create({\n profileId,\n leadTimeDays: defaults.leadTimeDays ?? 0,\n minimumOrderAmount: defaults.minimumOrderAmount ?? 0,\n paymentTerms: defaults.paymentTerms ?? '',\n status: VendorStatus.ACTIVE,\n });\n await vendor.save();\n return vendor;\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all vendors belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of vendors for the tenant\n */\n async findByTenant(tenantId: string): Promise<Vendor[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global vendors (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 vendors\n */\n async findGlobal(): Promise<Vendor[]> {\n return queryGlobal<Vendor>(this);\n }\n\n /**\n * Find vendors for a tenant including global vendors.\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 vendors\n */\n async findWithGlobals(tenantId: string): Promise<Vendor[]> {\n return queryWithGlobals<Vendor>(this, tenantId, 'Vendor.findWithGlobals');\n }\n}\n"],"names":["CustomerStatus","CustomerType","VendorStatus","ContractType","ContractStatus","FulfillmentType","FulfillmentStatus","PaymentMethod","PaymentStatus","PayoutStatus","PaymentIntentStatus","InvoiceStatus","__decorateClass","tenantId","ContractLineItemCollection","FulfillmentCollection","FulfillmentLineItemCollection","InvoiceLineItemCollection","PaymentAllocationCollection","field","seq","PaymentCollection","InvoiceCollection"],"mappings":"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"}