@dainamite/billing 0.19.0 → 0.20.0
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/CHANGELOG.md +82 -0
- package/README.md +14 -0
- package/dist/modules/billing/api/export/account/[id]/route.js +5 -5
- package/dist/modules/billing/api/export/account/[id]/route.js.map +1 -1
- package/dist/modules/billing/api/invoices/add-line/route.js +1 -1
- package/dist/modules/billing/api/invoices/add-line/route.js.map +1 -1
- package/dist/modules/billing/api/invoices/edit-line/route.js +1 -1
- package/dist/modules/billing/api/invoices/edit-line/route.js.map +1 -1
- package/dist/modules/billing/api/invoices/remove-line/route.js +1 -1
- package/dist/modules/billing/api/invoices/remove-line/route.js.map +1 -1
- package/dist/modules/billing/api/run-outcomes/route.js +2 -2
- package/dist/modules/billing/api/run-outcomes/route.js.map +2 -2
- package/dist/modules/billing/api/runs/retry-failed/route.js +2 -2
- package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
- package/dist/modules/billing/api/runs/route.js +2 -2
- package/dist/modules/billing/api/runs/route.js.map +2 -2
- package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/accounts/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/invoices/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/items/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/runs/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/statements/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/topups/page.js.map +1 -1
- package/dist/modules/billing/backend/billing/transactions/page.js.map +1 -1
- package/dist/modules/billing/ce.js.map +2 -2
- package/dist/modules/billing/commands/invoices.js +4 -4
- package/dist/modules/billing/commands/invoices.js.map +2 -2
- package/dist/modules/billing/commands/runs.js +3 -3
- package/dist/modules/billing/commands/runs.js.map +2 -2
- package/dist/modules/billing/components/PrepaidPanel.js.map +1 -1
- package/dist/modules/billing/data/entities.js +56 -56
- package/dist/modules/billing/data/entities.js.map +2 -2
- package/dist/modules/billing/data/entityIds.js.map +2 -2
- package/dist/modules/billing/events.js +20 -0
- package/dist/modules/billing/events.js.map +2 -2
- package/dist/modules/billing/lib/billRunEngine.js +4 -4
- package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
- package/dist/modules/billing/lib/billRunLabel.js.map +1 -1
- package/dist/modules/billing/lib/prepaidPeriodClose.js.map +1 -1
- package/dist/modules/billing/lib/tenantLock.js.map +1 -1
- package/dist/modules/billing/workers/reap-stale-bill-runs.js +2 -2
- package/dist/modules/billing/workers/reap-stale-bill-runs.js.map +2 -2
- package/generated/entities/billing_account/index.ts +23 -0
- package/generated/entities/billing_account_balance/index.ts +13 -0
- package/generated/entities/billing_account_transaction/index.ts +21 -0
- package/generated/entities/billing_account_usage/index.ts +18 -0
- package/generated/entities/billing_draft_invoice_edit/index.ts +14 -0
- package/generated/entities/billing_item/index.ts +22 -0
- package/generated/entities/billing_run/index.ts +21 -0
- package/generated/entities/billing_run_outcome/index.ts +17 -0
- package/generated/entities/billing_statement/index.ts +21 -0
- package/generated/entities/billing_topup/index.ts +20 -0
- package/generated/entities.ids.generated.ts +28 -0
- package/package.json +11 -10
- package/src/modules/billing/__tests__/commands/invoices.test.ts +5 -5
- package/src/modules/billing/__tests__/entityRegistry.test.ts +319 -0
- package/src/modules/billing/__tests__/events.test.ts +142 -0
- package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +3 -3
- package/src/modules/billing/api/export/account/[id]/route.ts +7 -7
- package/src/modules/billing/api/invoices/add-line/route.ts +1 -1
- package/src/modules/billing/api/invoices/edit-line/route.ts +2 -2
- package/src/modules/billing/api/invoices/remove-line/route.ts +1 -1
- package/src/modules/billing/api/run-outcomes/route.ts +2 -2
- package/src/modules/billing/api/runs/retry-failed/route.ts +2 -2
- package/src/modules/billing/api/runs/route.ts +2 -2
- package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +1 -1
- package/src/modules/billing/backend/billing/accounts/page.tsx +1 -1
- package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +1 -1
- package/src/modules/billing/backend/billing/invoices/page.tsx +1 -1
- package/src/modules/billing/backend/billing/items/page.tsx +1 -1
- package/src/modules/billing/backend/billing/runs/[id]/page.tsx +1 -1
- package/src/modules/billing/backend/billing/runs/page.tsx +1 -1
- package/src/modules/billing/backend/billing/statements/[id]/page.tsx +1 -1
- package/src/modules/billing/backend/billing/statements/page.tsx +1 -1
- package/src/modules/billing/backend/billing/topups/page.tsx +1 -1
- package/src/modules/billing/backend/billing/transactions/page.tsx +1 -1
- package/src/modules/billing/ce.ts +8 -7
- package/src/modules/billing/commands/invoices.ts +9 -9
- package/src/modules/billing/commands/runs.ts +3 -3
- package/src/modules/billing/components/PrepaidPanel.tsx +1 -1
- package/src/modules/billing/data/entities.ts +31 -10
- package/src/modules/billing/data/entityIds.ts +16 -11
- package/src/modules/billing/events.ts +26 -0
- package/src/modules/billing/lib/billRunEngine.ts +12 -12
- package/src/modules/billing/lib/billRunLabel.ts +1 -1
- package/src/modules/billing/lib/prepaidPeriodClose.ts +3 -3
- package/src/modules/billing/lib/tenantLock.ts +1 -1
- package/src/modules/billing/workers/reap-stale-bill-runs.ts +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/billing/data/entities.ts"],
|
|
4
|
-
"sourcesContent": ["import { OptionalProps } from '@mikro-orm/core'\nimport { Entity, Index, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'\n\n// \u2500\u2500\u2500 BillingAccount \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A container per customer (typically one customer = one account, but the\n// engine does not enforce uniqueness \u2014 multi-account-per-customer is a\n// legitimate model for B2B with separate budgets / currencies / branches).\n//\n// The `(organization_id, tenant_id, next_bill_date)` partial index is the\n// Bill Run hot path \u2014 every nightly run filters by tenant + org and orders\n// by next_bill_date. Multi-org tenants share `tenant_id` across orgs, so a\n// `(tenant_id, next_bill_date)`-only index would scan every other org's\n// rows for each tenant \u2014 fine at 1 org / tenant, painful at 50.\n\n@Entity({ tableName: 'billing_accounts' })\n@Index({\n name: 'billing_accounts_next_bill_date_idx',\n properties: ['organizationId', 'tenantId', 'nextBillDate'],\n expression:\n 'CREATE INDEX \"billing_accounts_next_bill_date_idx\" ON \"billing_accounts\" (\"organization_id\", \"tenant_id\", \"next_bill_date\") WHERE \"deleted_at\" IS NULL',\n})\n@Index({\n name: 'billing_accounts_customer_idx',\n properties: ['organizationId', 'tenantId', 'customerId'],\n})\n@Index({\n name: 'billing_accounts_sort_idx',\n properties: ['organizationId', 'tenantId', 'deletedAt', 'createdAt'],\n})\nexport class BillingAccount {\n [OptionalProps]?:\n | 'taxId'\n | 'lastBillDate'\n | 'billingMode'\n | 'creditLimit'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // String FK to customers.person.id (or any external customer system).\n // Kept as text so the package stays installable without the customers\n // module \u2014 see SPEC-001 cross-package data rule.\n @Property({ name: 'customer_id', type: 'text' })\n customerId!: string\n\n @Property({ type: 'text' })\n name!: string\n\n // ISO 4217 currency code. An account is single-currency by design \u2014\n // multi-currency = multiple accounts.\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // 'monthly' | 'quarterly' | 'annually' | 'weekly'. Validated at API ingress.\n @Property({ name: 'bill_cycle', type: 'text' })\n billCycle!: string\n\n // Day-of-month for monthly/quarterly/annually (1\u201328), or weekday code\n // ('mon'\u2026'sun') for weekly. Text so both shapes coexist.\n @Property({ name: 'bill_cycle_anchor', type: 'text' })\n billCycleAnchor!: string\n\n @Property({ name: 'invoice_email', type: 'text' })\n invoiceEmail!: string\n\n // ISO 639-1 (`en`, `pl`, \u2026). Used by mailer / template renderer.\n @Property({ name: 'invoice_language', type: 'text' })\n invoiceLanguage!: string\n\n // NIP / VATIN / TaxID. Nullable for individuals without a registered tax ID.\n @Property({ name: 'tax_id', type: 'text', nullable: true })\n taxId?: string | null\n\n // { line1, line2?, city, postal_code, country } \u2014 JSONB because address\n // shape differs across countries and we never query by address fields.\n @Property({ name: 'invoice_address', type: 'jsonb' })\n invoiceAddress!: Record<string, unknown>\n\n // Anchor of the NEXT bill period to be processed (advanced by Bill Run\n // on success). Date-only \u2014 calendar-aware cycle arithmetic.\n @Property({ name: 'next_bill_date', type: 'date', columnType: 'date' })\n nextBillDate!: Date\n\n // Anchor of the most recently billed period; null for fresh accounts.\n @Property({ name: 'last_bill_date', type: 'date', columnType: 'date', nullable: true })\n lastBillDate?: Date | null\n\n // Billing mode \u2014 'postpaid' (default; the shipped Bill Run \u2192 draft \u2192\n // post flow) or 'prepaid' (top up a balance, draw it down in real time;\n // SPEC-002). Immutable after create (like currency_code); validated at\n // API ingress. Existing rows default to postpaid \u2014 zero behavior change.\n @Property({ name: 'billing_mode', type: 'text', default: 'postpaid' })\n billingMode: string = 'postpaid'\n\n // Credit line extended to this customer (both modes). Prepaid: permitted\n // overdraft below zero. Postpaid: max outstanding AR exposure. `0` = no\n // credit. Reported, never enforced by billing (SPEC-002 assumption #11).\n @Property({ name: 'credit_limit', type: 'numeric', columnType: 'numeric(18, 4)', default: '0' })\n creditLimit: string = '0'\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingItem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A single line that will appear on an invoice. Three flavours:\n// `one_time` / `recurring` / `usage` \u2014 see rate_json shape per type in\n// data/validators.ts.\n//\n// `(tenant_id, bill_account_id, source_ref)` is unique for idempotency.\n// Postgres treats NULLs as distinct, so items without a source_ref do not\n// conflict \u2014 that is intentional (CSV imports / manual entries).\n\n@Entity({ tableName: 'billing_items' })\n@Unique({\n name: 'billing_items_source_ref_unique',\n properties: ['tenantId', 'billAccountId', 'sourceRef'],\n})\n@Index({\n name: 'billing_items_account_type_idx',\n properties: ['billAccountId', 'type'],\n})\n@Index({\n name: 'billing_items_account_active_idx',\n properties: ['billAccountId', 'type', 'isActive'],\n expression:\n 'CREATE INDEX \"billing_items_account_active_idx\" ON \"billing_items\" (\"bill_account_id\", \"type\", \"is_active\") WHERE \"deleted_at\" IS NULL',\n})\n@Index({\n name: 'billing_items_subscription_idx',\n properties: ['organizationId', 'tenantId', 'subscriptionId'],\n})\nexport class BillingItem {\n [OptionalProps]?:\n | 'billEndDate'\n | 'uomCode'\n | 'subscriptionId'\n | 'subscriptionItemId'\n | 'sourceRef'\n | 'currencyMismatch'\n | 'billedToDate'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // FK string to BillingAccount.id (intra-module; no ORM relation kept to\n // avoid coupling \u2014 we resolve at runtime).\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'one_time' | 'recurring' | 'usage'.\n @Property({ type: 'text' })\n type!: string\n\n @Property({ name: 'bill_start_date', type: 'date', columnType: 'date' })\n billStartDate!: Date\n\n // Null = open-ended (typical for ongoing recurring items).\n @Property({ name: 'bill_end_date', type: 'date', columnType: 'date', nullable: true })\n billEndDate?: Date | null\n\n @Property({ type: 'text' })\n description!: string\n\n // Pricing definition. Shape validated by Zod per `type`; see\n // data/validators.ts.\n @Property({ name: 'rate_json', type: 'jsonb' })\n rateJson!: Record<string, unknown>\n\n // Required for type='usage' (must match a Usage record's uom_code\n // exactly \u2014 no conversion). Ignored for other types.\n @Property({ name: 'uom_code', type: 'text', nullable: true })\n uomCode?: string | null\n\n // String FK to upstream subscription (e.g. CPQ's\n // cpq_inventory_subscriptions.id). Null for CSV / manual / non-subscription\n // contexts.\n @Property({ name: 'subscription_id', type: 'text', nullable: true })\n subscriptionId?: string | null\n\n @Property({ name: 'subscription_item_id', type: 'text', nullable: true })\n subscriptionItemId?: string | null\n\n // Idempotency key \u2014 duplicate POST returns the existing row.\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // Set when the integrator POSTed a value in a different currency than the\n // account's currency_code. Operator sees a warning on the draft.\n @Property({ name: 'currency_mismatch', type: 'boolean', default: false })\n currencyMismatch: boolean = false\n\n // Anti-double-bill guard at the item level \u2014 see spec\n // \"Why two anti-duplicate guards\".\n @Property({ name: 'billed_to_date', type: 'date', columnType: 'date', nullable: true })\n billedToDate?: Date | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingAccountUsage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Bucket for pre-aggregated usage uploaded by external systems. The\n// selection index `(bill_account_id, uom_code, rated_in_bill_run_id,\n// period_end)` matches the Bill Run \"find unrated records\" predicate\n// exactly \u2014 keep it in sync if that predicate changes.\n\n@Entity({ tableName: 'billing_account_usage' })\n@Unique({\n name: 'billing_account_usage_source_ref_unique',\n properties: ['tenantId', 'billAccountId', 'sourceRef'],\n})\n@Index({\n name: 'billing_account_usage_rating_idx',\n properties: ['billAccountId', 'uomCode', 'ratedInBillRunId', 'periodEnd'],\n})\nexport class BillingAccountUsage {\n [OptionalProps]?:\n | 'lineDescription'\n | 'sourceRef'\n | 'ratedInBillRunId'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // Must match the `uom_code` on a corresponding `type='usage'` Billing Item.\n @Property({ name: 'uom_code', type: 'text' })\n uomCode!: string\n\n // numeric(18,4) \u2192 string in app code (standard MikroORM numeric handling).\n @Property({ type: 'numeric', columnType: 'numeric(18, 4)' })\n quantity!: string\n\n @Property({ name: 'period_start', type: Date })\n periodStart!: Date\n\n @Property({ name: 'period_end', type: Date })\n periodEnd!: Date\n\n // Optional per-record description. When set, Bill Run emits one invoice\n // line per Usage record with this description. When null, the run\n // aggregates all matching Usage records into a single line using the\n // Billing Item's `description`.\n @Property({ name: 'line_description', type: 'text', nullable: true })\n lineDescription?: string | null\n\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // Set when consumed; prevents double-rating on retry.\n @Property({ name: 'rated_in_bill_run_id', type: 'uuid', nullable: true })\n ratedInBillRunId?: string | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillRun \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A single execution of the engine \u2014 one row per trigger (schedule or\n// manual). The concurrency lock is a Postgres transaction-scoped advisory\n// lock keyed by hash(tenant_id || 'billing-run'); it is NOT stored on this\n// row. Crash recovery for zombie `status='running'` rows is handled by\n// workers/reap-stale-bill-runs.ts.\n\n@Entity({ tableName: 'billing_runs' })\n@Index({\n name: 'billing_runs_tenant_status_idx',\n properties: ['tenantId', 'status', 'startedAt'],\n})\n@Index({\n name: 'billing_runs_parent_idx',\n properties: ['parentRunId'],\n})\nexport class BillRun {\n [OptionalProps]?:\n | 'parentRunId'\n | 'dryRun'\n | 'testMode'\n | 'catchUp'\n | 'scopedAccountIds'\n | 'startedAt'\n | 'finishedAt'\n | 'summary'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // 'schedule' | 'manual'.\n @Property({ name: 'triggered_by', type: 'text' })\n triggeredBy!: string\n\n // Set when this run is a \"retry failed\" of a previous run.\n @Property({ name: 'parent_run_id', type: 'uuid', nullable: true })\n parentRunId?: string | null\n\n // True \u2192 compute everything but persist nothing (preview).\n @Property({ name: 'dry_run', type: 'boolean', default: false })\n dryRun: boolean = false\n\n // True \u2192 persist drafts flagged as test, but do not advance state.\n @Property({ name: 'test_mode', type: 'boolean', default: false })\n testMode: boolean = false\n\n // True \u2192 loop missed cycles per account in one run (manual only).\n @Property({ name: 'catch_up', type: 'boolean', default: false })\n catchUp: boolean = false\n\n // Optional \u2014 run processes only these accounts when set. JSONB\n // (Postgres arrays would work too, but jsonb keeps tooling uniform\n // across this package).\n @Property({ name: 'scoped_account_ids', type: 'jsonb', nullable: true })\n scopedAccountIds?: string[] | null\n\n // The \"today\" the run uses to evaluate `next_bill_date <= as_of_date`.\n @Property({ name: 'as_of_date', type: 'date', columnType: 'date' })\n asOfDate!: Date\n\n @Property({ name: 'started_at', type: Date, nullable: true })\n startedAt?: Date | null\n\n @Property({ name: 'finished_at', type: Date, nullable: true })\n finishedAt?: Date | null\n\n // 'running' | 'completed' | 'partial_failure' | 'failed'.\n @Property({ type: 'text' })\n status!: string\n\n // { accounts_processed, drafts_created, drafts_skipped_existing,\n // accounts_failed, accounts_with_warnings, usage_records_rated }\n @Property({ type: 'jsonb', nullable: true })\n summary?: Record<string, unknown> | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillRunOutcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Per-account outcome of a Bill Run. Stored in its own table (not as a\n// JSONB array on BillRun.summary) so operators can query failures across\n// runs without jsonb_array_elements gymnastics. See spec rationale.\n\n@Entity({ tableName: 'billing_run_outcomes' })\n@Index({\n name: 'billing_run_outcomes_run_status_idx',\n properties: ['billRunId', 'status'],\n})\n@Index({\n name: 'billing_run_outcomes_account_history_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'createdAt'],\n})\nexport class BillRunOutcome {\n [OptionalProps]?:\n | 'errorMessage'\n | 'warnings'\n | 'draftInvoiceId'\n | 'statementId'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_run_id', type: 'uuid' })\n billRunId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'success' | 'success_with_warnings' | 'skipped_existing_draft' | 'failed'.\n @Property({ type: 'text' })\n status!: string\n\n @Property({ name: 'error_message', type: 'text', nullable: true })\n errorMessage?: string | null\n\n // E.g. `{ unmatched_usage_uoms: [\"api_request\"] }`.\n @Property({ type: 'jsonb', nullable: true })\n warnings?: Record<string, unknown> | null\n\n // FK string to the core/sales invoice (drafted by the run). Null when\n // the run skipped (status='skipped_existing_draft' or 'failed').\n @Property({ name: 'draft_invoice_id', type: 'uuid', nullable: true })\n draftInvoiceId?: string | null\n\n // FK (string) to the BillingStatement produced for a PREPAID account\n // (SPEC-002). Prepaid accounts get a statement instead of a draft invoice\n // at period close, so this is the prepaid analogue of `draft_invoice_id`.\n @Property({ name: 'statement_id', type: 'uuid', nullable: true })\n statementId?: string | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 DraftInvoiceEdit (audit) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Append-only audit of every operator edit on a draft invoice line.\n// Pattern mirrors `cpq_subscription_change_logs` \u2014 minimal columns\n// (no updated_at / is_active), append-only, soft-delete reserved for\n// compliance erasure only.\n\n@Entity({ tableName: 'billing_draft_invoice_edits' })\n@Index({\n name: 'billing_draft_invoice_edits_invoice_idx',\n properties: ['organizationId', 'tenantId', 'invoiceId', 'createdAt'],\n})\n@Index({\n name: 'billing_draft_invoice_edits_user_idx',\n properties: ['organizationId', 'tenantId', 'userId', 'createdAt'],\n})\nexport class DraftInvoiceEdit {\n [OptionalProps]?: 'invoiceLineId' | 'beforeJson' | 'afterJson' | 'createdAt' | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // FK string to the core/sales invoice (no ORM relation across packages).\n @Property({ name: 'invoice_id', type: 'uuid' })\n invoiceId!: string\n\n // Null for `line_added` / `line_removed` actions whose target line does\n // not yet (or no longer) exist on the invoice.\n @Property({ name: 'invoice_line_id', type: 'uuid', nullable: true })\n invoiceLineId?: string | null\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n // 'line_added' | 'line_removed' | 'line_edited'.\n @Property({ type: 'text' })\n action!: string\n\n // Snapshot before the edit. Null for `line_added` (no prior state).\n @Property({ name: 'before_json', type: 'jsonb', nullable: true })\n beforeJson?: Record<string, unknown> | null\n\n // Snapshot after the edit. Null for `line_removed` (no resulting state).\n @Property({ name: 'after_json', type: 'jsonb', nullable: true })\n afterJson?: Record<string, unknown> | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n // Reserved for compliance erasure only; runtime never deletes audit rows\n // through normal flows.\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingAccountBalance (SPEC-002) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The denormalized running balance for a prepaid account \u2014 exactly one\n// row per prepaid account. It is the O(1) read AND the row the atomic\n// `UPDATE \u2026 RETURNING` debit/credit locks, which is why the balance is\n// NOT a column on `billing_accounts`: the high-write balance update never\n// touches the account row that the Bill Run hot-path index sits on.\n//\n// `balance` is the cache; the source of truth is\n// `SUM(billing_account_transactions.amount)`. The reconciliation invariant\n// `balance == SUM(amount) == latest.balance_after` is unit-tested.\n\n@Entity({ tableName: 'billing_account_balances' })\n@Unique({\n name: 'billing_account_balances_account_unique',\n properties: ['tenantId', 'billAccountId'],\n})\n@Index({\n name: 'billing_account_balances_balance_idx',\n properties: ['organizationId', 'tenantId', 'balance'],\n})\nexport class BillingAccountBalance {\n [OptionalProps]?:\n | 'lowBalanceThreshold'\n | 'lastMovementAt'\n | 'createdAt'\n | 'updatedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // Unique FK (string) to BillingAccount.id \u2014 one balance per account.\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // Denormalized from the account so the balance row is self-contained.\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // Running balance. May be NEGATIVE (usage is never rejected). Maintained\n // exclusively by the atomic `UPDATE \u2026 RETURNING` in lib/balanceLedger.ts.\n @Property({ name: 'balance', type: 'numeric', columnType: 'numeric(18, 4)', default: '0' })\n balance: string = '0'\n\n // When `balance <= threshold` \u2192 status `low` + `billing.balance.low`.\n // Null = use the tenant config default\n // (`billing.prepaid.low_balance_threshold_default`).\n @Property({\n name: 'low_balance_threshold',\n type: 'numeric',\n columnType: 'numeric(18, 4)',\n nullable: true,\n })\n lowBalanceThreshold?: string | null\n\n // Timestamp of the most recent transaction \u2014 a cheap \"is this account\n // active?\" signal.\n @Property({ name: 'last_movement_at', type: Date, nullable: true })\n lastMovementAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n}\n\n// \u2500\u2500\u2500 BillingAccountTransaction (SPEC-002, append-only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The source of truth for every balance movement. Append-only \u2014 pattern\n// mirrors DraftInvoiceEdit (no `updated_at` / `is_active`; `deleted_at`\n// reserved for compliance erasure only). `balance = SUM(amount)`.\n//\n// The unique `(tenant_id, bill_account_id, source_ref)` is the idempotency\n// gate: a deduplicated usage upload or a retried recurring charge can never\n// double-debit (Postgres treats NULL source_ref as distinct \u2014 manual\n// adjustments without a key always insert).\n\n@Entity({ tableName: 'billing_account_transactions' })\n@Unique({\n name: 'billing_account_transactions_source_ref_unique',\n properties: ['tenantId', 'billAccountId', 'sourceRef'],\n})\n@Index({\n name: 'billing_account_transactions_history_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'createdAt'],\n})\n@Index({\n name: 'billing_account_transactions_account_type_idx',\n properties: ['billAccountId', 'type', 'createdAt'],\n})\nexport class BillingAccountTransaction {\n [OptionalProps]?:\n | 'usageId'\n | 'billingItemId'\n | 'topupId'\n | 'statementId'\n | 'sourceRef'\n | 'metadata'\n | 'userId'\n | 'createdAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'topup' | 'usage' | 'recurring' | 'one_time' | 'adjustment' | 'reversal'.\n @Property({ type: 'text' })\n type!: string\n\n // SIGNED: `> 0` credit, `< 0` debit. Balance = SUM(amount).\n @Property({ type: 'numeric', columnType: 'numeric(18, 4)' })\n amount!: string\n\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // Running balance AFTER this transaction \u2014 snapshotted from the atomic\n // `UPDATE \u2026 RETURNING`. Enables audit + fast statement math without\n // re-summing the whole ledger.\n @Property({ name: 'balance_after', type: 'numeric', columnType: 'numeric(18, 4)' })\n balanceAfter!: string\n\n // FK (string) to BillingAccountUsage for type='usage'.\n @Property({ name: 'usage_id', type: 'uuid', nullable: true })\n usageId?: string | null\n\n // FK (string) to the Billing Item that priced this debit.\n @Property({ name: 'billing_item_id', type: 'uuid', nullable: true })\n billingItemId?: string | null\n\n // FK (string) to BillingTopup for type='topup'.\n @Property({ name: 'topup_id', type: 'uuid', nullable: true })\n topupId?: string | null\n\n // FK (string) to the BillingStatement a period-close charge was rolled into.\n @Property({ name: 'statement_id', type: 'uuid', nullable: true })\n statementId?: string | null\n\n @Property({ type: 'text' })\n description!: string\n\n // Idempotency key, unique per (tenant_id, bill_account_id, source_ref).\n // usage debits = the usage record's source_ref; recurring =\n // `recurring-{itemId}-{periodEndISO}`; topup = `topup-{paymentId}`.\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // e.g. usage_tier_breakdown, uom_code, bill_period_start/end, gateway ids.\n @Property({ type: 'jsonb', nullable: true })\n metadata?: Record<string, unknown> | null\n\n // Operator for manual `adjustment` / `reversal`.\n @Property({ name: 'user_id', type: 'uuid', nullable: true })\n userId?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n // Compliance erasure only.\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingTopup (SPEC-002 \u2014 the \"registered top-up\") \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// One row per top-up attempt with a lifecycle (pending \u2192 captured | failed\n// | expired | cancelled), linked to its GatewayTransaction, its balance\n// credit transaction, and its VAT receipt invoice. \"The balance went up\"\n// is always explainable.\n\n@Entity({ tableName: 'billing_topups' })\n@Unique({\n name: 'billing_topups_payment_id_unique',\n properties: ['tenantId', 'paymentId'],\n})\n@Index({\n name: 'billing_topups_account_history_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'createdAt'],\n})\n@Index({\n name: 'billing_topups_status_idx',\n properties: ['tenantId', 'status'],\n})\nexport class BillingTopup {\n [OptionalProps]?:\n | 'gatewayTransactionId'\n | 'transactionId'\n | 'receiptInvoiceId'\n | 'sourceRef'\n | 'metadata'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'pending' | 'captured' | 'failed' | 'expired' | 'cancelled'.\n @Property({ type: 'text' })\n status!: string\n\n @Property({ type: 'numeric', columnType: 'numeric(18, 4)' })\n amount!: string\n\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n @Property({ name: 'provider_key', type: 'text' })\n providerKey!: string\n\n // The UUID we pass to createPaymentSession and match the captured event\n // on. Unique per tenant.\n @Property({ name: 'payment_id', type: 'uuid' })\n paymentId!: string\n\n // FK (string) to payment_gateways.GatewayTransaction once the session is\n // created.\n @Property({ name: 'gateway_transaction_id', type: 'uuid', nullable: true })\n gatewayTransactionId?: string | null\n\n // FK (string) to the credit BillingAccountTransaction written on capture.\n @Property({ name: 'transaction_id', type: 'uuid', nullable: true })\n transactionId?: string | null\n\n // FK (string) to the core/sales VAT receipt invoice.\n @Property({ name: 'receipt_invoice_id', type: 'uuid', nullable: true })\n receiptInvoiceId?: string | null\n\n // Optional idempotency for the initiating request.\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // success_url / cancel_url, provider payload echoes.\n @Property({ type: 'jsonb', nullable: true })\n metadata?: Record<string, unknown> | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingStatement (SPEC-002 \u2014 period close) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A non-fiscal consumption summary for a prepaid account's bill period.\n// Chains cleanly: each statement's `opening_balance` equals the prior\n// statement's `closing_balance`. Anti-duplicate via the unique\n// (tenant_id, bill_account_id, period_start, period_end).\n\n@Entity({ tableName: 'billing_statements' })\n@Unique({\n name: 'billing_statements_period_unique',\n properties: ['tenantId', 'billAccountId', 'periodStart', 'periodEnd'],\n})\n@Index({\n name: 'billing_statements_account_period_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'periodEnd'],\n})\nexport class BillingStatement {\n [OptionalProps]?:\n | 'billRunId'\n | 'totalAdjustments'\n | 'createdAt'\n | 'updatedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // FK (string) to the BillRun that generated it (period close reuses the\n // run engine). Null for a manually-generated statement.\n @Property({ name: 'bill_run_id', type: 'uuid', nullable: true })\n billRunId?: string | null\n\n @Property({ name: 'period_start', type: 'date', columnType: 'date' })\n periodStart!: Date\n\n @Property({ name: 'period_end', type: 'date', columnType: 'date' })\n periodEnd!: Date\n\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // Balance at period_start (= the prior statement's closing_balance).\n @Property({ name: 'opening_balance', type: 'numeric', columnType: 'numeric(18, 4)' })\n openingBalance!: string\n\n // Sum of credits in window.\n @Property({ name: 'total_topups', type: 'numeric', columnType: 'numeric(18, 4)' })\n totalTopups!: string\n\n // Sum of metered (real-time) usage debits in window (absolute).\n @Property({ name: 'total_usage', type: 'numeric', columnType: 'numeric(18, 4)' })\n totalUsage!: string\n\n // Sum of recurring + one_time charges debited at this close (absolute).\n @Property({ name: 'total_recurring', type: 'numeric', columnType: 'numeric(18, 4)' })\n totalRecurring!: string\n\n // Signed sum of manual adjustment + reversal movements in the window. Kept as\n // its own term so the statement stays self-consistent (closing reproduces\n // from the shown components) and the opening\u2192closing chain never drifts.\n @Property({ name: 'total_adjustments', type: 'numeric', columnType: 'numeric(18, 4)', default: '0' })\n totalAdjustments: string = '0'\n\n // opening + topups + adjustments \u2212 usage \u2212 recurring (= the period's net).\n @Property({ name: 'closing_balance', type: 'numeric', columnType: 'numeric(18, 4)' })\n closingBalance!: string\n\n // Per billing_item_id: { billing_type, uom_code?, quantity, amount }.\n @Property({ name: 'debit_breakdown', type: 'jsonb' })\n debitBreakdown!: Record<string, unknown>\n\n // 'generated' | 'sent'.\n @Property({ type: 'text' })\n status!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { OptionalProps } from '@mikro-orm/core'\nimport { Entity, Index, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'\n\n// \u2500\u2500\u2500 BillingAccount \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A container per customer (typically one customer = one account, but the\n// engine does not enforce uniqueness \u2014 multi-account-per-customer is a\n// legitimate model for B2B with separate budgets / currencies / branches).\n//\n// The `(organization_id, tenant_id, next_bill_date)` partial index is the\n// Bill Run hot path \u2014 every nightly run filters by tenant + org and orders\n// by next_bill_date. Multi-org tenants share `tenant_id` across orgs, so a\n// `(tenant_id, next_bill_date)`-only index would scan every other org's\n// rows for each tenant \u2014 fine at 1 org / tenant, painful at 50.\n\n@Entity({ tableName: 'billing_accounts' })\n@Index({\n name: 'billing_accounts_next_bill_date_idx',\n properties: ['organizationId', 'tenantId', 'nextBillDate'],\n expression:\n 'CREATE INDEX \"billing_accounts_next_bill_date_idx\" ON \"billing_accounts\" (\"organization_id\", \"tenant_id\", \"next_bill_date\") WHERE \"deleted_at\" IS NULL',\n})\n@Index({\n name: 'billing_accounts_customer_idx',\n properties: ['organizationId', 'tenantId', 'customerId'],\n})\n@Index({\n name: 'billing_accounts_sort_idx',\n properties: ['organizationId', 'tenantId', 'deletedAt', 'createdAt'],\n})\nexport class BillingAccount {\n [OptionalProps]?:\n | 'taxId'\n | 'lastBillDate'\n | 'billingMode'\n | 'creditLimit'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // String FK to customers.person.id (or any external customer system).\n // Kept as text so the package stays installable without the customers\n // module \u2014 see SPEC-001 cross-package data rule.\n @Property({ name: 'customer_id', type: 'text' })\n customerId!: string\n\n @Property({ type: 'text' })\n name!: string\n\n // ISO 4217 currency code. An account is single-currency by design \u2014\n // multi-currency = multiple accounts.\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // 'monthly' | 'quarterly' | 'annually' | 'weekly'. Validated at API ingress.\n @Property({ name: 'bill_cycle', type: 'text' })\n billCycle!: string\n\n // Day-of-month for monthly/quarterly/annually (1\u201328), or weekday code\n // ('mon'\u2026'sun') for weekly. Text so both shapes coexist.\n @Property({ name: 'bill_cycle_anchor', type: 'text' })\n billCycleAnchor!: string\n\n @Property({ name: 'invoice_email', type: 'text' })\n invoiceEmail!: string\n\n // ISO 639-1 (`en`, `pl`, \u2026). Used by mailer / template renderer.\n @Property({ name: 'invoice_language', type: 'text' })\n invoiceLanguage!: string\n\n // NIP / VATIN / TaxID. Nullable for individuals without a registered tax ID.\n @Property({ name: 'tax_id', type: 'text', nullable: true })\n taxId?: string | null\n\n // { line1, line2?, city, postal_code, country } \u2014 JSONB because address\n // shape differs across countries and we never query by address fields.\n @Property({ name: 'invoice_address', type: 'jsonb' })\n invoiceAddress!: Record<string, unknown>\n\n // Anchor of the NEXT bill period to be processed (advanced by Bill Run\n // on success). Date-only \u2014 calendar-aware cycle arithmetic.\n @Property({ name: 'next_bill_date', type: 'date', columnType: 'date' })\n nextBillDate!: Date\n\n // Anchor of the most recently billed period; null for fresh accounts.\n @Property({ name: 'last_bill_date', type: 'date', columnType: 'date', nullable: true })\n lastBillDate?: Date | null\n\n // Billing mode \u2014 'postpaid' (default; the shipped Bill Run \u2192 draft \u2192\n // post flow) or 'prepaid' (top up a balance, draw it down in real time;\n // SPEC-002). Immutable after create (like currency_code); validated at\n // API ingress. Existing rows default to postpaid \u2014 zero behavior change.\n @Property({ name: 'billing_mode', type: 'text', default: 'postpaid' })\n billingMode: string = 'postpaid'\n\n // Credit line extended to this customer (both modes). Prepaid: permitted\n // overdraft below zero. Postpaid: max outstanding AR exposure. `0` = no\n // credit. Reported, never enforced by billing (SPEC-002 assumption #11).\n @Property({ name: 'credit_limit', type: 'numeric', columnType: 'numeric(18, 4)', default: '0' })\n creditLimit: string = '0'\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingItem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A single line that will appear on an invoice. Three flavours:\n// `one_time` / `recurring` / `usage` \u2014 see rate_json shape per type in\n// data/validators.ts.\n//\n// `(tenant_id, bill_account_id, source_ref)` is unique for idempotency.\n// Postgres treats NULLs as distinct, so items without a source_ref do not\n// conflict \u2014 that is intentional (CSV imports / manual entries).\n\n@Entity({ tableName: 'billing_items' })\n@Unique({\n name: 'billing_items_source_ref_unique',\n properties: ['tenantId', 'billAccountId', 'sourceRef'],\n})\n@Index({\n name: 'billing_items_account_type_idx',\n properties: ['billAccountId', 'type'],\n})\n@Index({\n name: 'billing_items_account_active_idx',\n properties: ['billAccountId', 'type', 'isActive'],\n expression:\n 'CREATE INDEX \"billing_items_account_active_idx\" ON \"billing_items\" (\"bill_account_id\", \"type\", \"is_active\") WHERE \"deleted_at\" IS NULL',\n})\n@Index({\n name: 'billing_items_subscription_idx',\n properties: ['organizationId', 'tenantId', 'subscriptionId'],\n})\nexport class BillingItem {\n [OptionalProps]?:\n | 'billEndDate'\n | 'uomCode'\n | 'subscriptionId'\n | 'subscriptionItemId'\n | 'sourceRef'\n | 'currencyMismatch'\n | 'billedToDate'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // FK string to BillingAccount.id (intra-module; no ORM relation kept to\n // avoid coupling \u2014 we resolve at runtime).\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'one_time' | 'recurring' | 'usage'.\n @Property({ type: 'text' })\n type!: string\n\n @Property({ name: 'bill_start_date', type: 'date', columnType: 'date' })\n billStartDate!: Date\n\n // Null = open-ended (typical for ongoing recurring items).\n @Property({ name: 'bill_end_date', type: 'date', columnType: 'date', nullable: true })\n billEndDate?: Date | null\n\n @Property({ type: 'text' })\n description!: string\n\n // Pricing definition. Shape validated by Zod per `type`; see\n // data/validators.ts.\n @Property({ name: 'rate_json', type: 'jsonb' })\n rateJson!: Record<string, unknown>\n\n // Required for type='usage' (must match a Usage record's uom_code\n // exactly \u2014 no conversion). Ignored for other types.\n @Property({ name: 'uom_code', type: 'text', nullable: true })\n uomCode?: string | null\n\n // String FK to upstream subscription (e.g. CPQ's\n // cpq_inventory_subscriptions.id). Null for CSV / manual / non-subscription\n // contexts.\n @Property({ name: 'subscription_id', type: 'text', nullable: true })\n subscriptionId?: string | null\n\n @Property({ name: 'subscription_item_id', type: 'text', nullable: true })\n subscriptionItemId?: string | null\n\n // Idempotency key \u2014 duplicate POST returns the existing row.\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // Set when the integrator POSTed a value in a different currency than the\n // account's currency_code. Operator sees a warning on the draft.\n @Property({ name: 'currency_mismatch', type: 'boolean', default: false })\n currencyMismatch: boolean = false\n\n // Anti-double-bill guard at the item level \u2014 see spec\n // \"Why two anti-duplicate guards\".\n @Property({ name: 'billed_to_date', type: 'date', columnType: 'date', nullable: true })\n billedToDate?: Date | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingAccountUsage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Bucket for pre-aggregated usage uploaded by external systems. The\n// selection index `(bill_account_id, uom_code, rated_in_bill_run_id,\n// period_end)` matches the Bill Run \"find unrated records\" predicate\n// exactly \u2014 keep it in sync if that predicate changes.\n\n@Entity({ tableName: 'billing_account_usage' })\n@Unique({\n name: 'billing_account_usage_source_ref_unique',\n properties: ['tenantId', 'billAccountId', 'sourceRef'],\n})\n@Index({\n name: 'billing_account_usage_rating_idx',\n properties: ['billAccountId', 'uomCode', 'ratedInBillRunId', 'periodEnd'],\n})\nexport class BillingAccountUsage {\n [OptionalProps]?:\n | 'lineDescription'\n | 'sourceRef'\n | 'ratedInBillRunId'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // Must match the `uom_code` on a corresponding `type='usage'` Billing Item.\n @Property({ name: 'uom_code', type: 'text' })\n uomCode!: string\n\n // numeric(18,4) \u2192 string in app code (standard MikroORM numeric handling).\n @Property({ type: 'numeric', columnType: 'numeric(18, 4)' })\n quantity!: string\n\n @Property({ name: 'period_start', type: Date })\n periodStart!: Date\n\n @Property({ name: 'period_end', type: Date })\n periodEnd!: Date\n\n // Optional per-record description. When set, Bill Run emits one invoice\n // line per Usage record with this description. When null, the run\n // aggregates all matching Usage records into a single line using the\n // Billing Item's `description`.\n @Property({ name: 'line_description', type: 'text', nullable: true })\n lineDescription?: string | null\n\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // Set when consumed; prevents double-rating on retry.\n @Property({ name: 'rated_in_bill_run_id', type: 'uuid', nullable: true })\n ratedInBillRunId?: string | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingRun \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A single execution of the engine \u2014 one row per trigger (schedule or\n// manual). The concurrency lock is a Postgres transaction-scoped advisory\n// lock keyed by hash(tenant_id || 'billing-run'); it is NOT stored on this\n// row. Crash recovery for zombie `status='running'` rows is handled by\n// workers/reap-stale-bill-runs.ts.\n//\n// Class names must be the PascalCase of the entity-id suffix (`billing:\n// billing_run` \u2192 `BillingRun`) \u2014 that is how Open Mercato maps an id to its\n// table. Until 0.20.0 this class was `BillRun` (likewise `BillRunOutcome`,\n// `DraftInvoiceEdit`), and those three ids resolved to no table; see\n// data/entityIds.ts. Type-only aliases for the old names are at the end of\n// this file.\n\n@Entity({ tableName: 'billing_runs' })\n@Index({\n name: 'billing_runs_tenant_status_idx',\n properties: ['tenantId', 'status', 'startedAt'],\n})\n@Index({\n name: 'billing_runs_parent_idx',\n properties: ['parentRunId'],\n})\nexport class BillingRun {\n [OptionalProps]?:\n | 'parentRunId'\n | 'dryRun'\n | 'testMode'\n | 'catchUp'\n | 'scopedAccountIds'\n | 'startedAt'\n | 'finishedAt'\n | 'summary'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // 'schedule' | 'manual'.\n @Property({ name: 'triggered_by', type: 'text' })\n triggeredBy!: string\n\n // Set when this run is a \"retry failed\" of a previous run.\n @Property({ name: 'parent_run_id', type: 'uuid', nullable: true })\n parentRunId?: string | null\n\n // True \u2192 compute everything but persist nothing (preview).\n @Property({ name: 'dry_run', type: 'boolean', default: false })\n dryRun: boolean = false\n\n // True \u2192 persist drafts flagged as test, but do not advance state.\n @Property({ name: 'test_mode', type: 'boolean', default: false })\n testMode: boolean = false\n\n // True \u2192 loop missed cycles per account in one run (manual only).\n @Property({ name: 'catch_up', type: 'boolean', default: false })\n catchUp: boolean = false\n\n // Optional \u2014 run processes only these accounts when set. JSONB\n // (Postgres arrays would work too, but jsonb keeps tooling uniform\n // across this package).\n @Property({ name: 'scoped_account_ids', type: 'jsonb', nullable: true })\n scopedAccountIds?: string[] | null\n\n // The \"today\" the run uses to evaluate `next_bill_date <= as_of_date`.\n @Property({ name: 'as_of_date', type: 'date', columnType: 'date' })\n asOfDate!: Date\n\n @Property({ name: 'started_at', type: Date, nullable: true })\n startedAt?: Date | null\n\n @Property({ name: 'finished_at', type: Date, nullable: true })\n finishedAt?: Date | null\n\n // 'running' | 'completed' | 'partial_failure' | 'failed'.\n @Property({ type: 'text' })\n status!: string\n\n // { accounts_processed, drafts_created, drafts_skipped_existing,\n // accounts_failed, accounts_with_warnings, usage_records_rated }\n @Property({ type: 'jsonb', nullable: true })\n summary?: Record<string, unknown> | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingRunOutcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Per-account outcome of a Bill Run. Stored in its own table (not as a\n// JSONB array on BillingRun.summary) so operators can query failures across\n// runs without jsonb_array_elements gymnastics. See spec rationale.\n\n@Entity({ tableName: 'billing_run_outcomes' })\n@Index({\n name: 'billing_run_outcomes_run_status_idx',\n properties: ['billRunId', 'status'],\n})\n@Index({\n name: 'billing_run_outcomes_account_history_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'createdAt'],\n})\nexport class BillingRunOutcome {\n [OptionalProps]?:\n | 'errorMessage'\n | 'warnings'\n | 'draftInvoiceId'\n | 'statementId'\n | 'isActive'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_run_id', type: 'uuid' })\n billRunId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'success' | 'success_with_warnings' | 'skipped_existing_draft' | 'failed'.\n @Property({ type: 'text' })\n status!: string\n\n @Property({ name: 'error_message', type: 'text', nullable: true })\n errorMessage?: string | null\n\n // E.g. `{ unmatched_usage_uoms: [\"api_request\"] }`.\n @Property({ type: 'jsonb', nullable: true })\n warnings?: Record<string, unknown> | null\n\n // FK string to the core/sales invoice (drafted by the run). Null when\n // the run skipped (status='skipped_existing_draft' or 'failed').\n @Property({ name: 'draft_invoice_id', type: 'uuid', nullable: true })\n draftInvoiceId?: string | null\n\n // FK (string) to the BillingStatement produced for a PREPAID account\n // (SPEC-002). Prepaid accounts get a statement instead of a draft invoice\n // at period close, so this is the prepaid analogue of `draft_invoice_id`.\n @Property({ name: 'statement_id', type: 'uuid', nullable: true })\n statementId?: string | null\n\n @Property({ name: 'is_active', type: 'boolean', default: true })\n isActive: boolean = true\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingDraftInvoiceEdit (audit) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Append-only audit of every operator edit on a draft invoice line.\n// Pattern mirrors `cpq_subscription_change_logs` \u2014 minimal columns\n// (no updated_at / is_active), append-only, soft-delete reserved for\n// compliance erasure only.\n\n@Entity({ tableName: 'billing_draft_invoice_edits' })\n@Index({\n name: 'billing_draft_invoice_edits_invoice_idx',\n properties: ['organizationId', 'tenantId', 'invoiceId', 'createdAt'],\n})\n@Index({\n name: 'billing_draft_invoice_edits_user_idx',\n properties: ['organizationId', 'tenantId', 'userId', 'createdAt'],\n})\nexport class BillingDraftInvoiceEdit {\n [OptionalProps]?: 'invoiceLineId' | 'beforeJson' | 'afterJson' | 'createdAt' | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // FK string to the core/sales invoice (no ORM relation across packages).\n @Property({ name: 'invoice_id', type: 'uuid' })\n invoiceId!: string\n\n // Null for `line_added` / `line_removed` actions whose target line does\n // not yet (or no longer) exist on the invoice.\n @Property({ name: 'invoice_line_id', type: 'uuid', nullable: true })\n invoiceLineId?: string | null\n\n @Property({ name: 'user_id', type: 'uuid' })\n userId!: string\n\n // 'line_added' | 'line_removed' | 'line_edited'.\n @Property({ type: 'text' })\n action!: string\n\n // Snapshot before the edit. Null for `line_added` (no prior state).\n @Property({ name: 'before_json', type: 'jsonb', nullable: true })\n beforeJson?: Record<string, unknown> | null\n\n // Snapshot after the edit. Null for `line_removed` (no resulting state).\n @Property({ name: 'after_json', type: 'jsonb', nullable: true })\n afterJson?: Record<string, unknown> | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n // Reserved for compliance erasure only; runtime never deletes audit rows\n // through normal flows.\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingAccountBalance (SPEC-002) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The denormalized running balance for a prepaid account \u2014 exactly one\n// row per prepaid account. It is the O(1) read AND the row the atomic\n// `UPDATE \u2026 RETURNING` debit/credit locks, which is why the balance is\n// NOT a column on `billing_accounts`: the high-write balance update never\n// touches the account row that the Bill Run hot-path index sits on.\n//\n// `balance` is the cache; the source of truth is\n// `SUM(billing_account_transactions.amount)`. The reconciliation invariant\n// `balance == SUM(amount) == latest.balance_after` is unit-tested.\n\n@Entity({ tableName: 'billing_account_balances' })\n@Unique({\n name: 'billing_account_balances_account_unique',\n properties: ['tenantId', 'billAccountId'],\n})\n@Index({\n name: 'billing_account_balances_balance_idx',\n properties: ['organizationId', 'tenantId', 'balance'],\n})\nexport class BillingAccountBalance {\n [OptionalProps]?:\n | 'lowBalanceThreshold'\n | 'lastMovementAt'\n | 'createdAt'\n | 'updatedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n // Unique FK (string) to BillingAccount.id \u2014 one balance per account.\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // Denormalized from the account so the balance row is self-contained.\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // Running balance. May be NEGATIVE (usage is never rejected). Maintained\n // exclusively by the atomic `UPDATE \u2026 RETURNING` in lib/balanceLedger.ts.\n @Property({ name: 'balance', type: 'numeric', columnType: 'numeric(18, 4)', default: '0' })\n balance: string = '0'\n\n // When `balance <= threshold` \u2192 status `low` + `billing.balance.low`.\n // Null = use the tenant config default\n // (`billing.prepaid.low_balance_threshold_default`).\n @Property({\n name: 'low_balance_threshold',\n type: 'numeric',\n columnType: 'numeric(18, 4)',\n nullable: true,\n })\n lowBalanceThreshold?: string | null\n\n // Timestamp of the most recent transaction \u2014 a cheap \"is this account\n // active?\" signal.\n @Property({ name: 'last_movement_at', type: Date, nullable: true })\n lastMovementAt?: Date | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n}\n\n// \u2500\u2500\u2500 BillingAccountTransaction (SPEC-002, append-only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The source of truth for every balance movement. Append-only \u2014 pattern\n// mirrors BillingDraftInvoiceEdit (no `updated_at` / `is_active`;\n// `deleted_at` reserved for compliance erasure only). `balance = SUM(amount)`.\n//\n// The unique `(tenant_id, bill_account_id, source_ref)` is the idempotency\n// gate: a deduplicated usage upload or a retried recurring charge can never\n// double-debit (Postgres treats NULL source_ref as distinct \u2014 manual\n// adjustments without a key always insert).\n\n@Entity({ tableName: 'billing_account_transactions' })\n@Unique({\n name: 'billing_account_transactions_source_ref_unique',\n properties: ['tenantId', 'billAccountId', 'sourceRef'],\n})\n@Index({\n name: 'billing_account_transactions_history_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'createdAt'],\n})\n@Index({\n name: 'billing_account_transactions_account_type_idx',\n properties: ['billAccountId', 'type', 'createdAt'],\n})\nexport class BillingAccountTransaction {\n [OptionalProps]?:\n | 'usageId'\n | 'billingItemId'\n | 'topupId'\n | 'statementId'\n | 'sourceRef'\n | 'metadata'\n | 'userId'\n | 'createdAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'topup' | 'usage' | 'recurring' | 'one_time' | 'adjustment' | 'reversal'.\n @Property({ type: 'text' })\n type!: string\n\n // SIGNED: `> 0` credit, `< 0` debit. Balance = SUM(amount).\n @Property({ type: 'numeric', columnType: 'numeric(18, 4)' })\n amount!: string\n\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // Running balance AFTER this transaction \u2014 snapshotted from the atomic\n // `UPDATE \u2026 RETURNING`. Enables audit + fast statement math without\n // re-summing the whole ledger.\n @Property({ name: 'balance_after', type: 'numeric', columnType: 'numeric(18, 4)' })\n balanceAfter!: string\n\n // FK (string) to BillingAccountUsage for type='usage'.\n @Property({ name: 'usage_id', type: 'uuid', nullable: true })\n usageId?: string | null\n\n // FK (string) to the Billing Item that priced this debit.\n @Property({ name: 'billing_item_id', type: 'uuid', nullable: true })\n billingItemId?: string | null\n\n // FK (string) to BillingTopup for type='topup'.\n @Property({ name: 'topup_id', type: 'uuid', nullable: true })\n topupId?: string | null\n\n // FK (string) to the BillingStatement a period-close charge was rolled into.\n @Property({ name: 'statement_id', type: 'uuid', nullable: true })\n statementId?: string | null\n\n @Property({ type: 'text' })\n description!: string\n\n // Idempotency key, unique per (tenant_id, bill_account_id, source_ref).\n // usage debits = the usage record's source_ref; recurring =\n // `recurring-{itemId}-{periodEndISO}`; topup = `topup-{paymentId}`.\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // e.g. usage_tier_breakdown, uom_code, bill_period_start/end, gateway ids.\n @Property({ type: 'jsonb', nullable: true })\n metadata?: Record<string, unknown> | null\n\n // Operator for manual `adjustment` / `reversal`.\n @Property({ name: 'user_id', type: 'uuid', nullable: true })\n userId?: string | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n // Compliance erasure only.\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingTopup (SPEC-002 \u2014 the \"registered top-up\") \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// One row per top-up attempt with a lifecycle (pending \u2192 captured | failed\n// | expired | cancelled), linked to its GatewayTransaction, its balance\n// credit transaction, and its VAT receipt invoice. \"The balance went up\"\n// is always explainable.\n\n@Entity({ tableName: 'billing_topups' })\n@Unique({\n name: 'billing_topups_payment_id_unique',\n properties: ['tenantId', 'paymentId'],\n})\n@Index({\n name: 'billing_topups_account_history_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'createdAt'],\n})\n@Index({\n name: 'billing_topups_status_idx',\n properties: ['tenantId', 'status'],\n})\nexport class BillingTopup {\n [OptionalProps]?:\n | 'gatewayTransactionId'\n | 'transactionId'\n | 'receiptInvoiceId'\n | 'sourceRef'\n | 'metadata'\n | 'createdAt'\n | 'updatedAt'\n | 'deletedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // 'pending' | 'captured' | 'failed' | 'expired' | 'cancelled'.\n @Property({ type: 'text' })\n status!: string\n\n @Property({ type: 'numeric', columnType: 'numeric(18, 4)' })\n amount!: string\n\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n @Property({ name: 'provider_key', type: 'text' })\n providerKey!: string\n\n // The UUID we pass to createPaymentSession and match the captured event\n // on. Unique per tenant.\n @Property({ name: 'payment_id', type: 'uuid' })\n paymentId!: string\n\n // FK (string) to payment_gateways.GatewayTransaction once the session is\n // created.\n @Property({ name: 'gateway_transaction_id', type: 'uuid', nullable: true })\n gatewayTransactionId?: string | null\n\n // FK (string) to the credit BillingAccountTransaction written on capture.\n @Property({ name: 'transaction_id', type: 'uuid', nullable: true })\n transactionId?: string | null\n\n // FK (string) to the core/sales VAT receipt invoice.\n @Property({ name: 'receipt_invoice_id', type: 'uuid', nullable: true })\n receiptInvoiceId?: string | null\n\n // Optional idempotency for the initiating request.\n @Property({ name: 'source_ref', type: 'text', nullable: true })\n sourceRef?: string | null\n\n // success_url / cancel_url, provider payload echoes.\n @Property({ type: 'jsonb', nullable: true })\n metadata?: Record<string, unknown> | null\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n\n @Property({ name: 'deleted_at', type: Date, nullable: true })\n deletedAt?: Date | null\n}\n\n// \u2500\u2500\u2500 BillingStatement (SPEC-002 \u2014 period close) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// A non-fiscal consumption summary for a prepaid account's bill period.\n// Chains cleanly: each statement's `opening_balance` equals the prior\n// statement's `closing_balance`. Anti-duplicate via the unique\n// (tenant_id, bill_account_id, period_start, period_end).\n\n@Entity({ tableName: 'billing_statements' })\n@Unique({\n name: 'billing_statements_period_unique',\n properties: ['tenantId', 'billAccountId', 'periodStart', 'periodEnd'],\n})\n@Index({\n name: 'billing_statements_account_period_idx',\n properties: ['organizationId', 'tenantId', 'billAccountId', 'periodEnd'],\n})\nexport class BillingStatement {\n [OptionalProps]?:\n | 'billRunId'\n | 'totalAdjustments'\n | 'createdAt'\n | 'updatedAt'\n\n @PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })\n id!: string\n\n @Property({ name: 'organization_id', type: 'uuid' })\n organizationId!: string\n\n @Property({ name: 'tenant_id', type: 'uuid' })\n tenantId!: string\n\n @Property({ name: 'bill_account_id', type: 'uuid' })\n billAccountId!: string\n\n // FK (string) to the BillingRun that generated it (period close reuses the\n // run engine). Null for a manually-generated statement.\n @Property({ name: 'bill_run_id', type: 'uuid', nullable: true })\n billRunId?: string | null\n\n @Property({ name: 'period_start', type: 'date', columnType: 'date' })\n periodStart!: Date\n\n @Property({ name: 'period_end', type: 'date', columnType: 'date' })\n periodEnd!: Date\n\n @Property({ name: 'currency_code', type: 'text' })\n currencyCode!: string\n\n // Balance at period_start (= the prior statement's closing_balance).\n @Property({ name: 'opening_balance', type: 'numeric', columnType: 'numeric(18, 4)' })\n openingBalance!: string\n\n // Sum of credits in window.\n @Property({ name: 'total_topups', type: 'numeric', columnType: 'numeric(18, 4)' })\n totalTopups!: string\n\n // Sum of metered (real-time) usage debits in window (absolute).\n @Property({ name: 'total_usage', type: 'numeric', columnType: 'numeric(18, 4)' })\n totalUsage!: string\n\n // Sum of recurring + one_time charges debited at this close (absolute).\n @Property({ name: 'total_recurring', type: 'numeric', columnType: 'numeric(18, 4)' })\n totalRecurring!: string\n\n // Signed sum of manual adjustment + reversal movements in the window. Kept as\n // its own term so the statement stays self-consistent (closing reproduces\n // from the shown components) and the opening\u2192closing chain never drifts.\n @Property({ name: 'total_adjustments', type: 'numeric', columnType: 'numeric(18, 4)', default: '0' })\n totalAdjustments: string = '0'\n\n // opening + topups + adjustments \u2212 usage \u2212 recurring (= the period's net).\n @Property({ name: 'closing_balance', type: 'numeric', columnType: 'numeric(18, 4)' })\n closingBalance!: string\n\n // Per billing_item_id: { billing_type, uom_code?, quantity, amount }.\n @Property({ name: 'debit_breakdown', type: 'jsonb' })\n debitBreakdown!: Record<string, unknown>\n\n // 'generated' | 'sent'.\n @Property({ type: 'text' })\n status!: string\n\n @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })\n createdAt: Date = new Date()\n\n @Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })\n updatedAt: Date = new Date()\n}\n\n// \u2500\u2500\u2500 Deprecated names (0.20.0) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Type-only on purpose: a runtime alias (`export const BillRun = BillingRun`)\n// would make the app's generated `entities` list carry the same class twice.\n// Code that used the old names as values (`em.find(BillRun, \u2026)`) must switch\n// to the new ones \u2014 TypeScript flags it.\n\n/** @deprecated Renamed to {@link BillingRun} in 0.20.0. */\nexport type BillRun = BillingRun\n/** @deprecated Renamed to {@link BillingRunOutcome} in 0.20.0. */\nexport type BillRunOutcome = BillingRunOutcome\n/** @deprecated Renamed to {@link BillingDraftInvoiceEdit} in 0.20.0. */\nexport type DraftInvoiceEdit = BillingDraftInvoiceEdit\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/billing/data/entityIds.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Canonical entity IDs for the billing module.\n
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * Canonical entity IDs for the billing module \u2014 the single source of truth.\n * Billing code (CRUD factory `entityId`, indexer `entityType`, response\n * enricher `targetEntity`, \u2026) imports from here; a package must not import\n * the app's `#generated/entities.ids.generated`.\n *\n * The build turns this object into the shipped registry\n * `generated/entities.ids.generated.ts` (scripts/generate-entity-registry.mjs),\n * which a consuming app's `mercato generate` reads to register these ids in\n * its own `E.billing`. Without that, the query index rejects billing writes\n * and `mercato entities install` treats the ids as virtual custom entities.\n *\n * Adding an entity: add it here, then `yarn workspace @dainamite/billing\n * build` and commit `generated/`. The id suffix must be the snake_case of\n * the entity class name (`billing_run` \u2192 `BillingRun`): that is how the\n * framework maps an id to its table, and the build fails otherwise. Do not\n * rely on its table-name fallback \u2014 on Open Mercato 0.9 with MikroORM 7 it\n * never matches (it iterates `MetadataStorage.getAll()` as an array, but\n * that returns a Map).\n */\nexport const billingEntityIds = {\n account: 'billing:billing_account',\n item: 'billing:billing_item',\n usage: 'billing:billing_account_usage',\n run: 'billing:billing_run',\n runOutcome: 'billing:billing_run_outcome',\n draftInvoiceEdit: 'billing:billing_draft_invoice_edit',\n // SPEC-002 (prepaid balance billing)\n balance: 'billing:billing_account_balance',\n transaction: 'billing:billing_account_transaction',\n topup: 'billing:billing_topup',\n statement: 'billing:billing_statement',\n} as const\n\nexport type BillingEntityId = (typeof billingEntityIds)[keyof typeof billingEntityIds]\n"],
|
|
5
|
+
"mappings": "AAoBO,MAAM,mBAAmB;AAAA,EAC9B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AAAA,EACL,YAAY;AAAA,EACZ,kBAAkB;AAAA;AAAA,EAElB,SAAS;AAAA,EACT,aAAa;AAAA,EACb,OAAO;AAAA,EACP,WAAW;AACb;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import { createModuleEvents } from "@open-mercato/shared/modules/events";
|
|
2
2
|
const events = [
|
|
3
|
+
// ─── CRUD (commands/*.ts) ────────────────────────────────────
|
|
4
|
+
{ id: "billing.account.created", label: "Billing Account Created", entity: "account", category: "crud" },
|
|
5
|
+
{ id: "billing.account.updated", label: "Billing Account Updated", entity: "account", category: "crud" },
|
|
6
|
+
{ id: "billing.account.deleted", label: "Billing Account Deleted", entity: "account", category: "crud" },
|
|
7
|
+
{ id: "billing.item.created", label: "Billing Item Created", entity: "item", category: "crud" },
|
|
8
|
+
{ id: "billing.item.updated", label: "Billing Item Updated", entity: "item", category: "crud" },
|
|
9
|
+
{ id: "billing.item.deleted", label: "Billing Item Deleted", entity: "item", category: "crud" },
|
|
10
|
+
{
|
|
11
|
+
// One per usage upload, on the same high-throughput path as
|
|
12
|
+
// `billing.usage.rated` — excluded from workflow triggers for the same
|
|
13
|
+
// reason (see Open Question #2).
|
|
14
|
+
id: "billing.usage.created",
|
|
15
|
+
label: "Billing Usage Record Created",
|
|
16
|
+
entity: "usage",
|
|
17
|
+
category: "crud",
|
|
18
|
+
excludeFromTriggers: true
|
|
19
|
+
},
|
|
20
|
+
{ id: "billing.transaction.created", label: "Billing Balance Transaction Created", entity: "transaction", category: "crud" },
|
|
21
|
+
{ id: "billing.topup.created", label: "Billing Top-up Created", entity: "topup", category: "crud" },
|
|
22
|
+
// ─── Lifecycle ───────────────────────────────────────────────
|
|
3
23
|
{
|
|
4
24
|
id: "billing.invoice.posted",
|
|
5
25
|
label: "Billing Invoice Posted",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/billing/events.ts"],
|
|
4
|
-
"sourcesContent": ["import { createModuleEvents } from '@open-mercato/shared/modules/events'\n\n/**\n * Billing module events.\n *\n * Emission contract:\n * - `billing.invoice.posted` fires once per posted draft AFTER the post\n * transaction commits (sequence number assigned, status set to `posted`\n * in core/sales). Consumed by mailers / accounting bridges / analytics.\n * - `billing.invoice.line_posted` fires once per line on a posted invoice\n * so analytics can aggregate without scanning core/sales lines directly.\n * - `billing.run.completed` fires once per Bill Run after all per-account\n * transactions finished (status is one of `completed` / `partial_failure`\n * / `failed`). Useful for \"did the nightly run go OK?\" alerts.\n */\nconst events = [\n {\n id: 'billing.invoice.posted',\n label: 'Billing Invoice Posted',\n entity: 'invoice',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.invoice.line_posted',\n label: 'Billing Invoice Line Posted',\n entity: 'invoice_line',\n category: 'lifecycle',\n },\n {\n id: 'billing.run.completed',\n label: 'Bill Run Completed',\n entity: 'bill_run',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n // \u2500\u2500\u2500 Prepaid balance billing (SPEC-002) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n {\n id: 'billing.balance.topped_up',\n label: 'Balance Topped Up',\n entity: 'balance',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.balance.low',\n label: 'Balance Low',\n entity: 'balance',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.balance.exhausted',\n label: 'Balance Exhausted',\n entity: 'balance',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n // Per-debit signal \u2014 useful for a live balance UI but potentially\n // high-volume, so excluded from workflow triggers (see Open Question #2).\n id: 'billing.usage.rated',\n label: 'Usage Rated',\n entity: 'transaction',\n category: 'lifecycle',\n excludeFromTriggers: true,\n },\n {\n id: 'billing.statement.generated',\n label: 'Consumption Statement Generated',\n entity: 'statement',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.credit.over_limit',\n label: 'Credit Over Limit',\n entity: 'credit',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.credit.near_limit',\n label: 'Credit Near Limit',\n entity: 'credit',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n] as const\n\nexport const eventsConfig = createModuleEvents({\n moduleId: 'billing',\n events,\n})\n\nexport const emitBillingEvent = eventsConfig.emit\nexport type BillingEventId = (typeof events)[number]['id']\n\nexport default eventsConfig\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,0BAA0B;
|
|
4
|
+
"sourcesContent": ["import { createModuleEvents } from '@open-mercato/shared/modules/events'\n\n/**\n * Billing module events.\n *\n * Emission contract:\n * - `billing.invoice.posted` fires once per posted draft AFTER the post\n * transaction commits (sequence number assigned, status set to `posted`\n * in core/sales). Consumed by mailers / accounting bridges / analytics.\n * - `billing.invoice.line_posted` fires once per line on a posted invoice\n * so analytics can aggregate without scanning core/sales lines directly.\n * - `billing.run.completed` fires once per Bill Run after all per-account\n * transactions finished (status is one of `completed` / `partial_failure`\n * / `failed`). Useful for \"did the nightly run go OK?\" alerts.\n *\n * CRUD events (`billing.<entity>.<action>`) are emitted by the data engine\n * from the commands' `emitCrudSideEffects({ events: { module: 'billing',\n * entity } })`. Only the (entity, action) pairs a command actually emits are\n * declared \u2014 `__tests__/events.test.ts` fails when a command emits one that\n * is missing here (the data engine would log \"Emitting undeclared event\").\n */\nconst events = [\n // \u2500\u2500\u2500 CRUD (commands/*.ts) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n { id: 'billing.account.created', label: 'Billing Account Created', entity: 'account', category: 'crud' },\n { id: 'billing.account.updated', label: 'Billing Account Updated', entity: 'account', category: 'crud' },\n { id: 'billing.account.deleted', label: 'Billing Account Deleted', entity: 'account', category: 'crud' },\n { id: 'billing.item.created', label: 'Billing Item Created', entity: 'item', category: 'crud' },\n { id: 'billing.item.updated', label: 'Billing Item Updated', entity: 'item', category: 'crud' },\n { id: 'billing.item.deleted', label: 'Billing Item Deleted', entity: 'item', category: 'crud' },\n {\n // One per usage upload, on the same high-throughput path as\n // `billing.usage.rated` \u2014 excluded from workflow triggers for the same\n // reason (see Open Question #2).\n id: 'billing.usage.created',\n label: 'Billing Usage Record Created',\n entity: 'usage',\n category: 'crud',\n excludeFromTriggers: true,\n },\n { id: 'billing.transaction.created', label: 'Billing Balance Transaction Created', entity: 'transaction', category: 'crud' },\n { id: 'billing.topup.created', label: 'Billing Top-up Created', entity: 'topup', category: 'crud' },\n // \u2500\u2500\u2500 Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n {\n id: 'billing.invoice.posted',\n label: 'Billing Invoice Posted',\n entity: 'invoice',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.invoice.line_posted',\n label: 'Billing Invoice Line Posted',\n entity: 'invoice_line',\n category: 'lifecycle',\n },\n {\n id: 'billing.run.completed',\n label: 'Bill Run Completed',\n entity: 'bill_run',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n // \u2500\u2500\u2500 Prepaid balance billing (SPEC-002) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n {\n id: 'billing.balance.topped_up',\n label: 'Balance Topped Up',\n entity: 'balance',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.balance.low',\n label: 'Balance Low',\n entity: 'balance',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.balance.exhausted',\n label: 'Balance Exhausted',\n entity: 'balance',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n // Per-debit signal \u2014 useful for a live balance UI but potentially\n // high-volume, so excluded from workflow triggers (see Open Question #2).\n id: 'billing.usage.rated',\n label: 'Usage Rated',\n entity: 'transaction',\n category: 'lifecycle',\n excludeFromTriggers: true,\n },\n {\n id: 'billing.statement.generated',\n label: 'Consumption Statement Generated',\n entity: 'statement',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.credit.over_limit',\n label: 'Credit Over Limit',\n entity: 'credit',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n {\n id: 'billing.credit.near_limit',\n label: 'Credit Near Limit',\n entity: 'credit',\n category: 'lifecycle',\n clientBroadcast: true,\n },\n] as const\n\nexport const eventsConfig = createModuleEvents({\n moduleId: 'billing',\n events,\n})\n\nexport const emitBillingEvent = eventsConfig.emit\nexport type BillingEventId = (typeof events)[number]['id']\n\nexport default eventsConfig\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,0BAA0B;AAqBnC,MAAM,SAAS;AAAA;AAAA,EAEb,EAAE,IAAI,2BAA2B,OAAO,2BAA2B,QAAQ,WAAW,UAAU,OAAO;AAAA,EACvG,EAAE,IAAI,2BAA2B,OAAO,2BAA2B,QAAQ,WAAW,UAAU,OAAO;AAAA,EACvG,EAAE,IAAI,2BAA2B,OAAO,2BAA2B,QAAQ,WAAW,UAAU,OAAO;AAAA,EACvG,EAAE,IAAI,wBAAwB,OAAO,wBAAwB,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC9F,EAAE,IAAI,wBAAwB,OAAO,wBAAwB,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC9F,EAAE,IAAI,wBAAwB,OAAO,wBAAwB,QAAQ,QAAQ,UAAU,OAAO;AAAA,EAC9F;AAAA;AAAA;AAAA;AAAA,IAIE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,qBAAqB;AAAA,EACvB;AAAA,EACA,EAAE,IAAI,+BAA+B,OAAO,uCAAuC,QAAQ,eAAe,UAAU,OAAO;AAAA,EAC3H,EAAE,IAAI,yBAAyB,OAAO,0BAA0B,QAAQ,SAAS,UAAU,OAAO;AAAA;AAAA,EAElG;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA;AAAA,EAEA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,qBAAqB;AAAA,EACvB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AACF;AAEO,MAAM,eAAe,mBAAmB;AAAA,EAC7C,UAAU;AAAA,EACV;AACF,CAAC;AAEM,MAAM,mBAAmB,aAAa;AAG7C,IAAO,iBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,8 +2,8 @@ import {
|
|
|
2
2
|
BillingAccount,
|
|
3
3
|
BillingAccountUsage,
|
|
4
4
|
BillingItem,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
BillingRun,
|
|
6
|
+
BillingRunOutcome
|
|
7
7
|
} from "../data/entities.js";
|
|
8
8
|
import { advanceNextBillDate, deriveBillPeriod, isCycleDue } from "./billPeriod.js";
|
|
9
9
|
import {
|
|
@@ -32,7 +32,7 @@ async function runBillRun(em, container, params) {
|
|
|
32
32
|
}
|
|
33
33
|
async function executeRunWithinLock(em, container, params) {
|
|
34
34
|
const now = /* @__PURE__ */ new Date();
|
|
35
|
-
const billRun = em.create(
|
|
35
|
+
const billRun = em.create(BillingRun, {
|
|
36
36
|
organizationId: params.organizationId,
|
|
37
37
|
tenantId: params.tenantId,
|
|
38
38
|
triggeredBy: params.triggeredBy,
|
|
@@ -60,7 +60,7 @@ async function executeRunWithinLock(em, container, params) {
|
|
|
60
60
|
if (processResult.status !== "failed") {
|
|
61
61
|
totalUsageRecordsRated += processResult.usageRecordsRated;
|
|
62
62
|
}
|
|
63
|
-
const outcome = em.create(
|
|
63
|
+
const outcome = em.create(BillingRunOutcome, {
|
|
64
64
|
organizationId: params.organizationId,
|
|
65
65
|
tenantId: params.tenantId,
|
|
66
66
|
billRunId: billRun.id,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/billing/lib/billRunEngine.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport {\n BillingAccount,\n BillingAccountUsage,\n BillingItem,\n BillRun,\n BillRunOutcome,\n} from '../data/entities'\nimport { advanceNextBillDate, deriveBillPeriod, isCycleDue } from './billPeriod'\nimport {\n selectItemsForPeriod,\n type SelectableItem,\n} from './itemSelector'\nimport {\n buildInvoiceLinesFromItems,\n writeDraftInvoice,\n type InvoiceLineDescriptor,\n type WriteMode,\n} from './invoiceWriter'\nimport { withTenantLock } from './tenantLock'\nimport { processUsageForAccount } from './usageRunner'\nimport { processPrepaidAccount } from './prepaidPeriodClose'\nimport type { DomainEvent } from './balanceEvents'\nimport { emitBillingEvent, type BillingEventId } from '../events'\nimport type { BillCycle } from '../data/validators'\n\n/**\n * Bill Run engine \u2014 orchestrates one execution of the billing cycle for\n * a single tenant.\n *\n * Concurrency / locking:\n * - The whole run is wrapped in `withTenantLock(...)`, which holds a\n * transaction-scoped Postgres advisory lock keyed by\n * `hash(tenant_id || ':billing-run')`. A second trigger fired while a\n * run is in flight short-circuits with `TenantLockBusyError` (the\n * command layer maps that to HTTP 409).\n * - Within the outer transaction, every account is processed inside a\n * nested `em.transactional(...)` (PG SAVEPOINT). A per-account failure\n * rolls back only that account's work, leaving siblings unaffected.\n *\n * Run modes:\n * - `real` (default) \u2014 persist drafts, advance `next_bill_date`, mark\n * items `billed_to_date`. Production path.\n * - `test` \u2014 persist drafts flagged with `metadata.test_run=true`, but\n * do NOT advance state. Repeatable.\n * - `dry` \u2014 compute everything; persist nothing. Preview only.\n *\n * Catch-up:\n * - Manual triggers may set `catchUp=true`. The engine then loops cycles\n * per account until `next_bill_date > as_of_date`, emitting one draft\n * per missed cycle (never combined).\n * - Scheduled triggers always process exactly ONE cycle per account per\n * run, regardless of how far behind the account is.\n *\n * Anti-duplicate guards (both run in every mode except `dry`):\n * 1. Open-draft check on `core/sales` invoice (per period boundary).\n * 2. `billed_to_date` per BillingItem.\n */\n\nexport type RunMode = WriteMode\n\nexport type RunBillRunParams = {\n tenantId: string\n organizationId: string\n asOfDate: Date\n triggeredBy: 'schedule' | 'manual'\n mode: RunMode\n scopedAccountIds?: string[]\n parentRunId?: string | null\n catchUp?: boolean\n}\n\nexport type RunBillRunResult = {\n billRun: BillRun\n outcomes: BillRunOutcome[]\n}\n\ntype RunOutput = RunBillRunResult & {\n /** Prepaid statement / crossing events to emit AFTER the outer tx commits. */\n pendingEvents: DomainEvent[]\n}\n\ntype AccountWarnings = {\n currency_mismatch_items?: string[]\n unmatched_usage_uoms?: string[]\n}\n\ntype AccountProcessSuccess = {\n status:\n | 'success'\n | 'success_with_warnings'\n | 'skipped_existing_draft'\n | 'skipped_existing_statement'\n warnings: AccountWarnings | Record<string, unknown> | null\n draftInvoiceId: string | null\n // SPEC-002: prepaid accounts produce a statement instead of a draft invoice.\n statementId?: string | null\n statementEvents?: DomainEvent[]\n cyclesEmitted: number\n usageRecordsRated: number\n}\n\ntype AccountProcessFailed = {\n status: 'failed'\n errorMessage: string\n}\n\ntype AccountProcessResult = AccountProcessSuccess | AccountProcessFailed\n\nconst SCHEDULED_CYCLE_LIMIT = 1\nconst CATCH_UP_HARD_CAP = 60 // safety net so a misconfigured account can't loop forever\n\nexport async function runBillRun(\n em: EntityManager,\n container: AwilixContainer,\n params: RunBillRunParams,\n): Promise<RunBillRunResult> {\n const output = await withTenantLock(\n em,\n { tenantId: params.tenantId, lockName: 'billing-run' },\n async (tem) => executeRunWithinLock(tem, container, params),\n )\n // Emit prepaid statement / crossing events ONLY after the outer transaction\n // (held by withTenantLock) has committed \u2014 emitting inside the lock would let\n // a consumer observe an uncommitted statement or a phantom event on rollback.\n for (const ev of output.pendingEvents) {\n await emitBillingEvent(ev.id as BillingEventId, ev.payload, { persistent: true })\n }\n return { billRun: output.billRun, outcomes: output.outcomes }\n}\n\nasync function executeRunWithinLock(\n em: EntityManager,\n container: AwilixContainer,\n params: RunBillRunParams,\n): Promise<RunOutput> {\n const now = new Date()\n const billRun = em.create(BillRun, {\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n triggeredBy: params.triggeredBy,\n parentRunId: params.parentRunId ?? null,\n dryRun: params.mode === 'dry',\n testMode: params.mode === 'test',\n catchUp: Boolean(params.catchUp),\n scopedAccountIds: params.scopedAccountIds ?? null,\n asOfDate: params.asOfDate,\n startedAt: now,\n status: 'running',\n summary: null,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(billRun)\n await em.flush()\n\n const accounts = await selectTargetAccounts(em, params)\n const outcomes: BillRunOutcome[] = []\n const pendingEvents: DomainEvent[] = []\n let totalUsageRecordsRated = 0\n\n for (const account of accounts) {\n const processResult = await processAccountSafely(em, container, billRun, account, params)\n if (processResult.status !== 'failed') {\n totalUsageRecordsRated += processResult.usageRecordsRated\n }\n const outcome = em.create(BillRunOutcome, {\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n billRunId: billRun.id,\n billAccountId: account.id,\n status: processResult.status,\n errorMessage: processResult.status === 'failed' ? processResult.errorMessage : null,\n warnings:\n processResult.status === 'failed'\n ? null\n : (processResult.warnings as Record<string, unknown> | null),\n draftInvoiceId: processResult.status === 'failed' ? null : processResult.draftInvoiceId,\n statementId: processResult.status === 'failed' ? null : (processResult.statementId ?? null),\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(outcome)\n await em.flush()\n outcomes.push(outcome)\n\n // Collect statement / crossing events \u2014 runBillRun emits them AFTER the\n // outer tx commits (see runBillRun), never from inside the lock.\n if (processResult.status !== 'failed' && processResult.statementEvents) {\n pendingEvents.push(...processResult.statementEvents)\n }\n }\n\n billRun.status = computeFinalRunStatus(outcomes)\n billRun.finishedAt = new Date()\n billRun.updatedAt = billRun.finishedAt\n billRun.summary = buildRunSummary(outcomes, accounts.length, totalUsageRecordsRated)\n await em.flush()\n\n return { billRun, outcomes, pendingEvents }\n}\n\nasync function selectTargetAccounts(\n em: EntityManager,\n params: RunBillRunParams,\n): Promise<BillingAccount[]> {\n const where: Record<string, unknown> = {\n tenantId: params.tenantId,\n organizationId: params.organizationId,\n deletedAt: null,\n nextBillDate: { $lte: params.asOfDate },\n }\n if (params.scopedAccountIds && params.scopedAccountIds.length > 0) {\n where.id = { $in: params.scopedAccountIds }\n }\n return em.find(BillingAccount, where as never)\n}\n\nasync function processAccountSafely(\n em: EntityManager,\n container: AwilixContainer,\n billRun: BillRun,\n account: BillingAccount,\n params: RunBillRunParams,\n): Promise<AccountProcessResult> {\n try {\n return await em.transactional(async (subEm) => {\n return processAccount(subEm, container, billRun, account, params)\n })\n } catch (err) {\n return {\n status: 'failed',\n errorMessage: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\nasync function processAccount(\n em: EntityManager,\n container: AwilixContainer,\n billRun: BillRun,\n account: BillingAccount,\n params: RunBillRunParams,\n): Promise<AccountProcessResult> {\n // SPEC-002: a prepaid account never gets a payable draft invoice. It still\n // runs in the Bill Run, but charges recurring / one_time items to the\n // balance and produces a non-fiscal consumption statement.\n if (account.billingMode === 'prepaid') {\n const result = await processPrepaidAccount(em, account, billRun, {\n asOfDate: params.asOfDate,\n mode: params.mode,\n triggeredBy: params.triggeredBy,\n catchUp: params.catchUp,\n })\n if (result.status === 'failed') {\n return { status: 'failed', errorMessage: result.errorMessage }\n }\n return {\n status: result.status,\n warnings: result.warnings,\n draftInvoiceId: null,\n statementId: result.statementId,\n statementEvents: result.statementEvents,\n cyclesEmitted: result.cyclesEmitted,\n usageRecordsRated: 0,\n }\n }\n\n const maxCycles =\n params.triggeredBy === 'schedule' || !params.catchUp\n ? SCHEDULED_CYCLE_LIMIT\n : CATCH_UP_HARD_CAP\n\n let cyclesEmitted = 0\n let lastDraftInvoiceId: string | null = null\n let totalUsageRatedThisAccount = 0\n const aggregatedWarnings: AccountWarnings = {}\n\n while (cyclesEmitted < maxCycles && isCycleDue(account.nextBillDate, params.asOfDate)) {\n const period = deriveBillPeriod(account.nextBillDate, account.billCycle as BillCycle)\n\n // Open-draft anti-duplicate \u2014 only applies when we'd actually persist.\n if (params.mode !== 'dry') {\n const existing = await findExistingDraftForPeriod(em, account, period)\n if (existing) {\n return {\n status: 'skipped_existing_draft',\n warnings: null,\n draftInvoiceId: existing,\n cyclesEmitted,\n usageRecordsRated: totalUsageRatedThisAccount,\n }\n }\n }\n\n const items = await em.find(BillingItem, {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n deletedAt: null,\n } as never)\n\n const usageItems = (items as BillingItem[]).filter((it) => it.type === 'usage')\n\n // Non-usage path: one_time + recurring filtered by the selector.\n const { included } = selectItemsForPeriod(items as SelectableItem[], period)\n const billableItems = included as unknown as BillingItem[]\n\n const currencyMismatchIds = billableItems\n .filter((it) => it.currencyMismatch)\n .map((it) => it.id)\n if (currencyMismatchIds.length > 0) {\n aggregatedWarnings.currency_mismatch_items = [\n ...(aggregatedWarnings.currency_mismatch_items ?? []),\n ...currencyMismatchIds,\n ]\n }\n const itemLines = buildInvoiceLinesFromItems(billableItems)\n\n // Usage path: aggregate matching usage records per uom_code, rate,\n // emit one line per matched usage item, warn about unmatched UoMs.\n const usageResult = await processUsageForAccount(em, account, period, usageItems)\n if (usageResult.unmatchedUoms.length > 0) {\n aggregatedWarnings.unmatched_usage_uoms = [\n ...(aggregatedWarnings.unmatched_usage_uoms ?? []),\n ...usageResult.unmatchedUoms,\n ]\n }\n\n const allLines: InvoiceLineDescriptor[] = [...itemLines, ...usageResult.lines]\n if (allLines.length === 0) {\n // Nothing to bill this cycle \u2014 still advance so the account moves\n // forward in real mode. In test / dry, no advance, so we'd loop\n // forever; break out.\n if (params.mode !== 'real') break\n account.nextBillDate = advanceNextBillDate(\n account.nextBillDate,\n account.billCycle as BillCycle,\n )\n account.lastBillDate = period.periodEnd\n account.updatedAt = new Date()\n cyclesEmitted += 1\n continue\n }\n\n const written = await writeDraftInvoice(em, container, {\n mode: params.mode,\n account,\n period,\n billRunId: billRun.id,\n lines: allLines,\n })\n lastDraftInvoiceId = written.invoiceId\n\n if (params.mode === 'real') {\n const now = new Date()\n for (const item of billableItems) {\n item.billedToDate = period.periodEnd\n item.updatedAt = now\n }\n // Mark consumed usage records \u2014 single bulk update so we don't\n // round-trip per record on accounts with thousands of usage rows.\n if (usageResult.matchedUoms.length > 0) {\n // Predicate-based bulk update: marks every still-unrated record\n // for the matched uoms in the account's period with a single\n // SQL UPDATE \u2014 no id list ever leaves Postgres. Stays\n // memory-bounded even for telco accounts with millions of\n // usage records per cycle (spec Performance Considerations\n // \"Shape B\").\n await em.nativeUpdate(\n BillingAccountUsage,\n {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n uomCode: { $in: usageResult.matchedUoms },\n ratedInBillRunId: null,\n periodEnd: { $lte: period.periodEnd },\n deletedAt: null,\n } as never,\n { ratedInBillRunId: billRun.id, updatedAt: now } as never,\n )\n }\n totalUsageRatedThisAccount += usageResult.ratedCount\n account.nextBillDate = advanceNextBillDate(\n account.nextBillDate,\n account.billCycle as BillCycle,\n )\n account.lastBillDate = period.periodEnd\n account.updatedAt = now\n await em.flush()\n } else {\n // Test / dry: do not advance state. One iteration only.\n break\n }\n\n cyclesEmitted += 1\n }\n\n const hasCurrencyWarnings =\n (aggregatedWarnings.currency_mismatch_items ?? []).length > 0\n const hasUnmatchedUsage =\n (aggregatedWarnings.unmatched_usage_uoms ?? []).length > 0\n const hasWarnings = hasCurrencyWarnings || hasUnmatchedUsage\n return {\n status: hasWarnings ? 'success_with_warnings' : 'success',\n warnings: hasWarnings ? aggregatedWarnings : null,\n draftInvoiceId: lastDraftInvoiceId,\n cyclesEmitted,\n usageRecordsRated: totalUsageRatedThisAccount,\n }\n}\n\nasync function findExistingDraftForPeriod(\n em: EntityManager,\n account: BillingAccount,\n period: { periodStart: Date; periodEnd: Date },\n): Promise<string | null> {\n const periodStartISO = period.periodStart.toISOString().slice(0, 10)\n const periodEndISO = period.periodEnd.toISOString().slice(0, 10)\n const rows = (await em.execute(\n `SELECT id FROM sales_invoices\n WHERE tenant_id = ?\n AND organization_id = ?\n AND deleted_at IS NULL\n AND status = 'draft'\n AND metadata->>'bill_account_id' = ?\n AND metadata->>'bill_period_start' = ?\n AND metadata->>'bill_period_end' = ?\n LIMIT 1`,\n [account.tenantId, account.organizationId, account.id, periodStartISO, periodEndISO],\n )) as unknown as Array<{ id: string }> | undefined\n if (!rows || rows.length === 0) return null\n return rows[0].id ?? null\n}\n\nfunction computeFinalRunStatus(\n outcomes: readonly BillRunOutcome[],\n): BillRun['status'] {\n if (outcomes.length === 0) return 'completed'\n const anyFailed = outcomes.some((o) => o.status === 'failed')\n const anySuccess = outcomes.some(\n (o) => o.status === 'success' || o.status === 'success_with_warnings',\n )\n if (anyFailed && anySuccess) return 'partial_failure'\n if (anyFailed) return 'partial_failure'\n return 'completed'\n}\n\nfunction buildRunSummary(\n outcomes: readonly BillRunOutcome[],\n totalAccounts: number,\n usageRecordsRated: number,\n): Record<string, unknown> {\n return {\n accounts_processed: totalAccounts,\n drafts_created: outcomes.filter(\n (o) => o.draftInvoiceId !== null && o.status !== 'skipped_existing_draft',\n ).length,\n drafts_skipped_existing: outcomes.filter((o) => o.status === 'skipped_existing_draft')\n .length,\n // SPEC-002 prepaid period close.\n statements_created: outcomes.filter(\n (o) => o.statementId !== null && o.status !== 'skipped_existing_statement',\n ).length,\n statements_skipped_existing: outcomes.filter(\n (o) => o.status === 'skipped_existing_statement',\n ).length,\n accounts_failed: outcomes.filter((o) => o.status === 'failed').length,\n accounts_with_warnings: outcomes.filter((o) => o.status === 'success_with_warnings')\n .length,\n usage_records_rated: usageRecordsRated,\n }\n}\n"],
|
|
5
|
-
"mappings": "AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,qBAAqB,kBAAkB,kBAAkB;AAClE;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,sBAAsB;AAC/B,SAAS,8BAA8B;AACvC,SAAS,6BAA6B;AAEtC,SAAS,wBAA6C;AAsFtD,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAE1B,eAAsB,WACpB,IACA,WACA,QAC2B;AAC3B,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA,EAAE,UAAU,OAAO,UAAU,UAAU,cAAc;AAAA,IACrD,OAAO,QAAQ,qBAAqB,KAAK,WAAW,MAAM;AAAA,EAC5D;AAIA,aAAW,MAAM,OAAO,eAAe;AACrC,UAAM,iBAAiB,GAAG,IAAsB,GAAG,SAAS,EAAE,YAAY,KAAK,CAAC;AAAA,EAClF;AACA,SAAO,EAAE,SAAS,OAAO,SAAS,UAAU,OAAO,SAAS;AAC9D;AAEA,eAAe,qBACb,IACA,WACA,QACoB;AACpB,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,UAAU,GAAG,OAAO,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport {\n BillingAccount,\n BillingAccountUsage,\n BillingItem,\n BillingRun,\n BillingRunOutcome,\n} from '../data/entities'\nimport { advanceNextBillDate, deriveBillPeriod, isCycleDue } from './billPeriod'\nimport {\n selectItemsForPeriod,\n type SelectableItem,\n} from './itemSelector'\nimport {\n buildInvoiceLinesFromItems,\n writeDraftInvoice,\n type InvoiceLineDescriptor,\n type WriteMode,\n} from './invoiceWriter'\nimport { withTenantLock } from './tenantLock'\nimport { processUsageForAccount } from './usageRunner'\nimport { processPrepaidAccount } from './prepaidPeriodClose'\nimport type { DomainEvent } from './balanceEvents'\nimport { emitBillingEvent, type BillingEventId } from '../events'\nimport type { BillCycle } from '../data/validators'\n\n/**\n * Bill Run engine \u2014 orchestrates one execution of the billing cycle for\n * a single tenant.\n *\n * Concurrency / locking:\n * - The whole run is wrapped in `withTenantLock(...)`, which holds a\n * transaction-scoped Postgres advisory lock keyed by\n * `hash(tenant_id || ':billing-run')`. A second trigger fired while a\n * run is in flight short-circuits with `TenantLockBusyError` (the\n * command layer maps that to HTTP 409).\n * - Within the outer transaction, every account is processed inside a\n * nested `em.transactional(...)` (PG SAVEPOINT). A per-account failure\n * rolls back only that account's work, leaving siblings unaffected.\n *\n * Run modes:\n * - `real` (default) \u2014 persist drafts, advance `next_bill_date`, mark\n * items `billed_to_date`. Production path.\n * - `test` \u2014 persist drafts flagged with `metadata.test_run=true`, but\n * do NOT advance state. Repeatable.\n * - `dry` \u2014 compute everything; persist nothing. Preview only.\n *\n * Catch-up:\n * - Manual triggers may set `catchUp=true`. The engine then loops cycles\n * per account until `next_bill_date > as_of_date`, emitting one draft\n * per missed cycle (never combined).\n * - Scheduled triggers always process exactly ONE cycle per account per\n * run, regardless of how far behind the account is.\n *\n * Anti-duplicate guards (both run in every mode except `dry`):\n * 1. Open-draft check on `core/sales` invoice (per period boundary).\n * 2. `billed_to_date` per BillingItem.\n */\n\nexport type RunMode = WriteMode\n\nexport type RunBillRunParams = {\n tenantId: string\n organizationId: string\n asOfDate: Date\n triggeredBy: 'schedule' | 'manual'\n mode: RunMode\n scopedAccountIds?: string[]\n parentRunId?: string | null\n catchUp?: boolean\n}\n\nexport type RunBillRunResult = {\n billRun: BillingRun\n outcomes: BillingRunOutcome[]\n}\n\ntype RunOutput = RunBillRunResult & {\n /** Prepaid statement / crossing events to emit AFTER the outer tx commits. */\n pendingEvents: DomainEvent[]\n}\n\ntype AccountWarnings = {\n currency_mismatch_items?: string[]\n unmatched_usage_uoms?: string[]\n}\n\ntype AccountProcessSuccess = {\n status:\n | 'success'\n | 'success_with_warnings'\n | 'skipped_existing_draft'\n | 'skipped_existing_statement'\n warnings: AccountWarnings | Record<string, unknown> | null\n draftInvoiceId: string | null\n // SPEC-002: prepaid accounts produce a statement instead of a draft invoice.\n statementId?: string | null\n statementEvents?: DomainEvent[]\n cyclesEmitted: number\n usageRecordsRated: number\n}\n\ntype AccountProcessFailed = {\n status: 'failed'\n errorMessage: string\n}\n\ntype AccountProcessResult = AccountProcessSuccess | AccountProcessFailed\n\nconst SCHEDULED_CYCLE_LIMIT = 1\nconst CATCH_UP_HARD_CAP = 60 // safety net so a misconfigured account can't loop forever\n\nexport async function runBillRun(\n em: EntityManager,\n container: AwilixContainer,\n params: RunBillRunParams,\n): Promise<RunBillRunResult> {\n const output = await withTenantLock(\n em,\n { tenantId: params.tenantId, lockName: 'billing-run' },\n async (tem) => executeRunWithinLock(tem, container, params),\n )\n // Emit prepaid statement / crossing events ONLY after the outer transaction\n // (held by withTenantLock) has committed \u2014 emitting inside the lock would let\n // a consumer observe an uncommitted statement or a phantom event on rollback.\n for (const ev of output.pendingEvents) {\n await emitBillingEvent(ev.id as BillingEventId, ev.payload, { persistent: true })\n }\n return { billRun: output.billRun, outcomes: output.outcomes }\n}\n\nasync function executeRunWithinLock(\n em: EntityManager,\n container: AwilixContainer,\n params: RunBillRunParams,\n): Promise<RunOutput> {\n const now = new Date()\n const billRun = em.create(BillingRun, {\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n triggeredBy: params.triggeredBy,\n parentRunId: params.parentRunId ?? null,\n dryRun: params.mode === 'dry',\n testMode: params.mode === 'test',\n catchUp: Boolean(params.catchUp),\n scopedAccountIds: params.scopedAccountIds ?? null,\n asOfDate: params.asOfDate,\n startedAt: now,\n status: 'running',\n summary: null,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(billRun)\n await em.flush()\n\n const accounts = await selectTargetAccounts(em, params)\n const outcomes: BillingRunOutcome[] = []\n const pendingEvents: DomainEvent[] = []\n let totalUsageRecordsRated = 0\n\n for (const account of accounts) {\n const processResult = await processAccountSafely(em, container, billRun, account, params)\n if (processResult.status !== 'failed') {\n totalUsageRecordsRated += processResult.usageRecordsRated\n }\n const outcome = em.create(BillingRunOutcome, {\n organizationId: params.organizationId,\n tenantId: params.tenantId,\n billRunId: billRun.id,\n billAccountId: account.id,\n status: processResult.status,\n errorMessage: processResult.status === 'failed' ? processResult.errorMessage : null,\n warnings:\n processResult.status === 'failed'\n ? null\n : (processResult.warnings as Record<string, unknown> | null),\n draftInvoiceId: processResult.status === 'failed' ? null : processResult.draftInvoiceId,\n statementId: processResult.status === 'failed' ? null : (processResult.statementId ?? null),\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(outcome)\n await em.flush()\n outcomes.push(outcome)\n\n // Collect statement / crossing events \u2014 runBillRun emits them AFTER the\n // outer tx commits (see runBillRun), never from inside the lock.\n if (processResult.status !== 'failed' && processResult.statementEvents) {\n pendingEvents.push(...processResult.statementEvents)\n }\n }\n\n billRun.status = computeFinalRunStatus(outcomes)\n billRun.finishedAt = new Date()\n billRun.updatedAt = billRun.finishedAt\n billRun.summary = buildRunSummary(outcomes, accounts.length, totalUsageRecordsRated)\n await em.flush()\n\n return { billRun, outcomes, pendingEvents }\n}\n\nasync function selectTargetAccounts(\n em: EntityManager,\n params: RunBillRunParams,\n): Promise<BillingAccount[]> {\n const where: Record<string, unknown> = {\n tenantId: params.tenantId,\n organizationId: params.organizationId,\n deletedAt: null,\n nextBillDate: { $lte: params.asOfDate },\n }\n if (params.scopedAccountIds && params.scopedAccountIds.length > 0) {\n where.id = { $in: params.scopedAccountIds }\n }\n return em.find(BillingAccount, where as never)\n}\n\nasync function processAccountSafely(\n em: EntityManager,\n container: AwilixContainer,\n billRun: BillingRun,\n account: BillingAccount,\n params: RunBillRunParams,\n): Promise<AccountProcessResult> {\n try {\n return await em.transactional(async (subEm) => {\n return processAccount(subEm, container, billRun, account, params)\n })\n } catch (err) {\n return {\n status: 'failed',\n errorMessage: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\nasync function processAccount(\n em: EntityManager,\n container: AwilixContainer,\n billRun: BillingRun,\n account: BillingAccount,\n params: RunBillRunParams,\n): Promise<AccountProcessResult> {\n // SPEC-002: a prepaid account never gets a payable draft invoice. It still\n // runs in the Bill Run, but charges recurring / one_time items to the\n // balance and produces a non-fiscal consumption statement.\n if (account.billingMode === 'prepaid') {\n const result = await processPrepaidAccount(em, account, billRun, {\n asOfDate: params.asOfDate,\n mode: params.mode,\n triggeredBy: params.triggeredBy,\n catchUp: params.catchUp,\n })\n if (result.status === 'failed') {\n return { status: 'failed', errorMessage: result.errorMessage }\n }\n return {\n status: result.status,\n warnings: result.warnings,\n draftInvoiceId: null,\n statementId: result.statementId,\n statementEvents: result.statementEvents,\n cyclesEmitted: result.cyclesEmitted,\n usageRecordsRated: 0,\n }\n }\n\n const maxCycles =\n params.triggeredBy === 'schedule' || !params.catchUp\n ? SCHEDULED_CYCLE_LIMIT\n : CATCH_UP_HARD_CAP\n\n let cyclesEmitted = 0\n let lastDraftInvoiceId: string | null = null\n let totalUsageRatedThisAccount = 0\n const aggregatedWarnings: AccountWarnings = {}\n\n while (cyclesEmitted < maxCycles && isCycleDue(account.nextBillDate, params.asOfDate)) {\n const period = deriveBillPeriod(account.nextBillDate, account.billCycle as BillCycle)\n\n // Open-draft anti-duplicate \u2014 only applies when we'd actually persist.\n if (params.mode !== 'dry') {\n const existing = await findExistingDraftForPeriod(em, account, period)\n if (existing) {\n return {\n status: 'skipped_existing_draft',\n warnings: null,\n draftInvoiceId: existing,\n cyclesEmitted,\n usageRecordsRated: totalUsageRatedThisAccount,\n }\n }\n }\n\n const items = await em.find(BillingItem, {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n deletedAt: null,\n } as never)\n\n const usageItems = (items as BillingItem[]).filter((it) => it.type === 'usage')\n\n // Non-usage path: one_time + recurring filtered by the selector.\n const { included } = selectItemsForPeriod(items as SelectableItem[], period)\n const billableItems = included as unknown as BillingItem[]\n\n const currencyMismatchIds = billableItems\n .filter((it) => it.currencyMismatch)\n .map((it) => it.id)\n if (currencyMismatchIds.length > 0) {\n aggregatedWarnings.currency_mismatch_items = [\n ...(aggregatedWarnings.currency_mismatch_items ?? []),\n ...currencyMismatchIds,\n ]\n }\n const itemLines = buildInvoiceLinesFromItems(billableItems)\n\n // Usage path: aggregate matching usage records per uom_code, rate,\n // emit one line per matched usage item, warn about unmatched UoMs.\n const usageResult = await processUsageForAccount(em, account, period, usageItems)\n if (usageResult.unmatchedUoms.length > 0) {\n aggregatedWarnings.unmatched_usage_uoms = [\n ...(aggregatedWarnings.unmatched_usage_uoms ?? []),\n ...usageResult.unmatchedUoms,\n ]\n }\n\n const allLines: InvoiceLineDescriptor[] = [...itemLines, ...usageResult.lines]\n if (allLines.length === 0) {\n // Nothing to bill this cycle \u2014 still advance so the account moves\n // forward in real mode. In test / dry, no advance, so we'd loop\n // forever; break out.\n if (params.mode !== 'real') break\n account.nextBillDate = advanceNextBillDate(\n account.nextBillDate,\n account.billCycle as BillCycle,\n )\n account.lastBillDate = period.periodEnd\n account.updatedAt = new Date()\n cyclesEmitted += 1\n continue\n }\n\n const written = await writeDraftInvoice(em, container, {\n mode: params.mode,\n account,\n period,\n billRunId: billRun.id,\n lines: allLines,\n })\n lastDraftInvoiceId = written.invoiceId\n\n if (params.mode === 'real') {\n const now = new Date()\n for (const item of billableItems) {\n item.billedToDate = period.periodEnd\n item.updatedAt = now\n }\n // Mark consumed usage records \u2014 single bulk update so we don't\n // round-trip per record on accounts with thousands of usage rows.\n if (usageResult.matchedUoms.length > 0) {\n // Predicate-based bulk update: marks every still-unrated record\n // for the matched uoms in the account's period with a single\n // SQL UPDATE \u2014 no id list ever leaves Postgres. Stays\n // memory-bounded even for telco accounts with millions of\n // usage records per cycle (spec Performance Considerations\n // \"Shape B\").\n await em.nativeUpdate(\n BillingAccountUsage,\n {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n uomCode: { $in: usageResult.matchedUoms },\n ratedInBillRunId: null,\n periodEnd: { $lte: period.periodEnd },\n deletedAt: null,\n } as never,\n { ratedInBillRunId: billRun.id, updatedAt: now } as never,\n )\n }\n totalUsageRatedThisAccount += usageResult.ratedCount\n account.nextBillDate = advanceNextBillDate(\n account.nextBillDate,\n account.billCycle as BillCycle,\n )\n account.lastBillDate = period.periodEnd\n account.updatedAt = now\n await em.flush()\n } else {\n // Test / dry: do not advance state. One iteration only.\n break\n }\n\n cyclesEmitted += 1\n }\n\n const hasCurrencyWarnings =\n (aggregatedWarnings.currency_mismatch_items ?? []).length > 0\n const hasUnmatchedUsage =\n (aggregatedWarnings.unmatched_usage_uoms ?? []).length > 0\n const hasWarnings = hasCurrencyWarnings || hasUnmatchedUsage\n return {\n status: hasWarnings ? 'success_with_warnings' : 'success',\n warnings: hasWarnings ? aggregatedWarnings : null,\n draftInvoiceId: lastDraftInvoiceId,\n cyclesEmitted,\n usageRecordsRated: totalUsageRatedThisAccount,\n }\n}\n\nasync function findExistingDraftForPeriod(\n em: EntityManager,\n account: BillingAccount,\n period: { periodStart: Date; periodEnd: Date },\n): Promise<string | null> {\n const periodStartISO = period.periodStart.toISOString().slice(0, 10)\n const periodEndISO = period.periodEnd.toISOString().slice(0, 10)\n const rows = (await em.execute(\n `SELECT id FROM sales_invoices\n WHERE tenant_id = ?\n AND organization_id = ?\n AND deleted_at IS NULL\n AND status = 'draft'\n AND metadata->>'bill_account_id' = ?\n AND metadata->>'bill_period_start' = ?\n AND metadata->>'bill_period_end' = ?\n LIMIT 1`,\n [account.tenantId, account.organizationId, account.id, periodStartISO, periodEndISO],\n )) as unknown as Array<{ id: string }> | undefined\n if (!rows || rows.length === 0) return null\n return rows[0].id ?? null\n}\n\nfunction computeFinalRunStatus(\n outcomes: readonly BillingRunOutcome[],\n): BillingRun['status'] {\n if (outcomes.length === 0) return 'completed'\n const anyFailed = outcomes.some((o) => o.status === 'failed')\n const anySuccess = outcomes.some(\n (o) => o.status === 'success' || o.status === 'success_with_warnings',\n )\n if (anyFailed && anySuccess) return 'partial_failure'\n if (anyFailed) return 'partial_failure'\n return 'completed'\n}\n\nfunction buildRunSummary(\n outcomes: readonly BillingRunOutcome[],\n totalAccounts: number,\n usageRecordsRated: number,\n): Record<string, unknown> {\n return {\n accounts_processed: totalAccounts,\n drafts_created: outcomes.filter(\n (o) => o.draftInvoiceId !== null && o.status !== 'skipped_existing_draft',\n ).length,\n drafts_skipped_existing: outcomes.filter((o) => o.status === 'skipped_existing_draft')\n .length,\n // SPEC-002 prepaid period close.\n statements_created: outcomes.filter(\n (o) => o.statementId !== null && o.status !== 'skipped_existing_statement',\n ).length,\n statements_skipped_existing: outcomes.filter(\n (o) => o.status === 'skipped_existing_statement',\n ).length,\n accounts_failed: outcomes.filter((o) => o.status === 'failed').length,\n accounts_with_warnings: outcomes.filter((o) => o.status === 'success_with_warnings')\n .length,\n usage_records_rated: usageRecordsRated,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/billing/lib/billRunLabel.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Human-readable label for a Bill Run.\n *\n * The
|
|
4
|
+
"sourcesContent": ["/**\n * Human-readable label for a Bill Run.\n *\n * The BillingRun entity has no `name` column \u2014 the engine keys runs by id.\n * The admin UI derives a stable, scannable label from the run's as-of\n * date so operators see `BILL_RUN_2026-05-20` instead of a raw UUID.\n */\nexport function billRunName(asOfDate: string | null | undefined): string {\n const date =\n typeof asOfDate === 'string' && asOfDate.length >= 10\n ? asOfDate.slice(0, 10)\n : 'unknown'\n return `BILL_RUN_${date}`\n}\n"],
|
|
5
5
|
"mappings": "AAOO,SAAS,YAAY,UAA6C;AACvE,QAAM,OACJ,OAAO,aAAa,YAAY,SAAS,UAAU,KAC/C,SAAS,MAAM,GAAG,EAAE,IACpB;AACN,SAAO,YAAY,IAAI;AACzB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|