@dainamite/billing 0.17.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 +658 -0
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/modules/billing/acl.js +20 -0
- package/dist/modules/billing/acl.js.map +7 -0
- package/dist/modules/billing/api/accounts/route.js +182 -0
- package/dist/modules/billing/api/accounts/route.js.map +7 -0
- package/dist/modules/billing/api/export/account/[id]/route.js +170 -0
- package/dist/modules/billing/api/export/account/[id]/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/[id]/route.js +85 -0
- package/dist/modules/billing/api/invoices/[id]/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/add-line/route.js +69 -0
- package/dist/modules/billing/api/invoices/add-line/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/edit-line/route.js +78 -0
- package/dist/modules/billing/api/invoices/edit-line/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/post/route.js +74 -0
- package/dist/modules/billing/api/invoices/post/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/remove-line/route.js +69 -0
- package/dist/modules/billing/api/invoices/remove-line/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/route.js +109 -0
- package/dist/modules/billing/api/invoices/route.js.map +7 -0
- package/dist/modules/billing/api/items/bulk/route.js +74 -0
- package/dist/modules/billing/api/items/bulk/route.js.map +7 -0
- package/dist/modules/billing/api/items/route.js +224 -0
- package/dist/modules/billing/api/items/route.js.map +7 -0
- package/dist/modules/billing/api/openapi.js +25 -0
- package/dist/modules/billing/api/openapi.js.map +7 -0
- package/dist/modules/billing/api/run-outcomes/route.js +122 -0
- package/dist/modules/billing/api/run-outcomes/route.js.map +7 -0
- package/dist/modules/billing/api/runs/retry-failed/route.js +70 -0
- package/dist/modules/billing/api/runs/retry-failed/route.js.map +7 -0
- package/dist/modules/billing/api/runs/route.js +143 -0
- package/dist/modules/billing/api/runs/route.js.map +7 -0
- package/dist/modules/billing/api/test-invoices/wipe/route.js +65 -0
- package/dist/modules/billing/api/test-invoices/wipe/route.js.map +7 -0
- package/dist/modules/billing/api/usage/route.js +138 -0
- package/dist/modules/billing/api/usage/route.js.map +7 -0
- package/dist/modules/billing/backend/billing/accounts/[id]/page.js +402 -0
- package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/accounts/[id]/page.meta.js +25 -0
- package/dist/modules/billing/backend/billing/accounts/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/accounts/create/page.js +82 -0
- package/dist/modules/billing/backend/billing/accounts/create/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/accounts/create/page.meta.js +25 -0
- package/dist/modules/billing/backend/billing/accounts/create/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/accounts/page.js +173 -0
- package/dist/modules/billing/backend/billing/accounts/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/accounts/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/accounts/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/invoices/[id]/page.js +500 -0
- package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/invoices/[id]/page.meta.js +25 -0
- package/dist/modules/billing/backend/billing/invoices/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/invoices/page.js +187 -0
- package/dist/modules/billing/backend/billing/invoices/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/invoices/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/invoices/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/items/[id]/page.js +265 -0
- package/dist/modules/billing/backend/billing/items/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/items/[id]/page.meta.js +25 -0
- package/dist/modules/billing/backend/billing/items/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/items/create/page.js +90 -0
- package/dist/modules/billing/backend/billing/items/create/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/items/create/page.meta.js +25 -0
- package/dist/modules/billing/backend/billing/items/create/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/items/page.js +208 -0
- package/dist/modules/billing/backend/billing/items/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/items/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/items/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/runs/[id]/page.js +293 -0
- package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/runs/[id]/page.meta.js +21 -0
- package/dist/modules/billing/backend/billing/runs/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/runs/page.js +341 -0
- package/dist/modules/billing/backend/billing/runs/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/runs/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/runs/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/usage/create/page.js +100 -0
- package/dist/modules/billing/backend/billing/usage/create/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/usage/create/page.meta.js +25 -0
- package/dist/modules/billing/backend/billing/usage/create/page.meta.js.map +7 -0
- package/dist/modules/billing/ce.js +56 -0
- package/dist/modules/billing/ce.js.map +7 -0
- package/dist/modules/billing/cli.js +67 -0
- package/dist/modules/billing/cli.js.map +7 -0
- package/dist/modules/billing/commands/accounts.js +157 -0
- package/dist/modules/billing/commands/accounts.js.map +7 -0
- package/dist/modules/billing/commands/index.js +6 -0
- package/dist/modules/billing/commands/index.js.map +7 -0
- package/dist/modules/billing/commands/invoices.js +401 -0
- package/dist/modules/billing/commands/invoices.js.map +7 -0
- package/dist/modules/billing/commands/items.js +317 -0
- package/dist/modules/billing/commands/items.js.map +7 -0
- package/dist/modules/billing/commands/runs.js +123 -0
- package/dist/modules/billing/commands/runs.js.map +7 -0
- package/dist/modules/billing/commands/usage.js +88 -0
- package/dist/modules/billing/commands/usage.js.map +7 -0
- package/dist/modules/billing/components/AccountForm.js +420 -0
- package/dist/modules/billing/components/AccountForm.js.map +7 -0
- package/dist/modules/billing/components/DetailFields.js +37 -0
- package/dist/modules/billing/components/DetailFields.js.map +7 -0
- package/dist/modules/billing/components/ItemForm.js +440 -0
- package/dist/modules/billing/components/ItemForm.js.map +7 -0
- package/dist/modules/billing/components/LineFormDialog.js +195 -0
- package/dist/modules/billing/components/LineFormDialog.js.map +7 -0
- package/dist/modules/billing/components/UsageForm.js +262 -0
- package/dist/modules/billing/components/UsageForm.js.map +7 -0
- package/dist/modules/billing/data/entities.js +433 -0
- package/dist/modules/billing/data/entities.js.map +7 -0
- package/dist/modules/billing/data/entityIds.js +12 -0
- package/dist/modules/billing/data/entityIds.js.map +7 -0
- package/dist/modules/billing/data/validators.js +396 -0
- package/dist/modules/billing/data/validators.js.map +7 -0
- package/dist/modules/billing/di.js +6 -0
- package/dist/modules/billing/di.js.map +7 -0
- package/dist/modules/billing/events.js +35 -0
- package/dist/modules/billing/events.js.map +7 -0
- package/dist/modules/billing/i18n/en.json +336 -0
- package/dist/modules/billing/i18n/pl.json +336 -0
- package/dist/modules/billing/index.js +17 -0
- package/dist/modules/billing/index.js.map +7 -0
- package/dist/modules/billing/lib/billPeriod.js +101 -0
- package/dist/modules/billing/lib/billPeriod.js.map +7 -0
- package/dist/modules/billing/lib/billRunEngine.js +256 -0
- package/dist/modules/billing/lib/billRunEngine.js.map +7 -0
- package/dist/modules/billing/lib/billRunLabel.js +8 -0
- package/dist/modules/billing/lib/billRunLabel.js.map +7 -0
- package/dist/modules/billing/lib/idempotency.js +13 -0
- package/dist/modules/billing/lib/idempotency.js.map +7 -0
- package/dist/modules/billing/lib/invoiceStatus.js +38 -0
- package/dist/modules/billing/lib/invoiceStatus.js.map +7 -0
- package/dist/modules/billing/lib/invoiceWriter.js +153 -0
- package/dist/modules/billing/lib/invoiceWriter.js.map +7 -0
- package/dist/modules/billing/lib/itemSelector.js +77 -0
- package/dist/modules/billing/lib/itemSelector.js.map +7 -0
- package/dist/modules/billing/lib/seeds/demo.js +130 -0
- package/dist/modules/billing/lib/seeds/demo.js.map +7 -0
- package/dist/modules/billing/lib/tenantLock.js +32 -0
- package/dist/modules/billing/lib/tenantLock.js.map +7 -0
- package/dist/modules/billing/lib/usageRater.js +150 -0
- package/dist/modules/billing/lib/usageRater.js.map +7 -0
- package/dist/modules/billing/lib/usageRunner.js +82 -0
- package/dist/modules/billing/lib/usageRunner.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20260519000000_billing.js +144 -0
- package/dist/modules/billing/migrations/Migration20260519000000_billing.js.map +7 -0
- package/dist/modules/billing/setup.js +190 -0
- package/dist/modules/billing/setup.js.map +7 -0
- package/dist/modules/billing/subscribers/payment-captured-to-paid.js +47 -0
- package/dist/modules/billing/subscribers/payment-captured-to-paid.js.map +7 -0
- package/dist/modules/billing/translations.js +5 -0
- package/dist/modules/billing/translations.js.map +7 -0
- package/dist/modules/billing/workers/reap-stale-bill-runs.js +31 -0
- package/dist/modules/billing/workers/reap-stale-bill-runs.js.map +7 -0
- package/package.json +148 -0
- package/src/modules/billing/__integration__/TC-BILL-001.spec.ts +116 -0
- package/src/modules/billing/__integration__/TC-BILL-002.spec.ts +169 -0
- package/src/modules/billing/__integration__/TC-BILL-003.spec.ts +28 -0
- package/src/modules/billing/__integration__/meta.ts +4 -0
- package/src/modules/billing/__tests__/commands/invoices.test.ts +464 -0
- package/src/modules/billing/__tests__/commands/itemsBulk.test.ts +284 -0
- package/src/modules/billing/__tests__/lib/billPeriod.test.ts +131 -0
- package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +561 -0
- package/src/modules/billing/__tests__/lib/idempotency.test.ts +81 -0
- package/src/modules/billing/__tests__/lib/invoiceStatus.test.ts +97 -0
- package/src/modules/billing/__tests__/lib/invoiceWriter.test.ts +119 -0
- package/src/modules/billing/__tests__/lib/itemSelector.test.ts +192 -0
- package/src/modules/billing/__tests__/lib/seedsDemo.test.ts +86 -0
- package/src/modules/billing/__tests__/lib/tenantLock.test.ts +126 -0
- package/src/modules/billing/__tests__/lib/usageRater.test.ts +190 -0
- package/src/modules/billing/__tests__/package-isolation.test.ts +223 -0
- package/src/modules/billing/__tests__/setup.test.ts +107 -0
- package/src/modules/billing/__tests__/validators.test.ts +594 -0
- package/src/modules/billing/acl.ts +16 -0
- package/src/modules/billing/api/accounts/route.ts +198 -0
- package/src/modules/billing/api/export/account/[id]/route.ts +233 -0
- package/src/modules/billing/api/invoices/[id]/route.ts +143 -0
- package/src/modules/billing/api/invoices/add-line/route.ts +74 -0
- package/src/modules/billing/api/invoices/edit-line/route.ts +97 -0
- package/src/modules/billing/api/invoices/post/route.ts +97 -0
- package/src/modules/billing/api/invoices/remove-line/route.ts +75 -0
- package/src/modules/billing/api/invoices/route.ts +167 -0
- package/src/modules/billing/api/items/bulk/route.ts +92 -0
- package/src/modules/billing/api/items/route.ts +255 -0
- package/src/modules/billing/api/openapi.ts +33 -0
- package/src/modules/billing/api/run-outcomes/route.ts +149 -0
- package/src/modules/billing/api/runs/retry-failed/route.ts +88 -0
- package/src/modules/billing/api/runs/route.ts +170 -0
- package/src/modules/billing/api/test-invoices/wipe/route.ts +87 -0
- package/src/modules/billing/api/usage/route.ts +148 -0
- package/src/modules/billing/backend/billing/accounts/[id]/page.meta.ts +23 -0
- package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +574 -0
- package/src/modules/billing/backend/billing/accounts/create/page.meta.ts +23 -0
- package/src/modules/billing/backend/billing/accounts/create/page.tsx +101 -0
- package/src/modules/billing/backend/billing/accounts/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/accounts/page.tsx +222 -0
- package/src/modules/billing/backend/billing/invoices/[id]/page.meta.ts +23 -0
- package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +613 -0
- package/src/modules/billing/backend/billing/invoices/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/invoices/page.tsx +245 -0
- package/src/modules/billing/backend/billing/items/[id]/page.meta.ts +23 -0
- package/src/modules/billing/backend/billing/items/[id]/page.tsx +366 -0
- package/src/modules/billing/backend/billing/items/create/page.meta.ts +23 -0
- package/src/modules/billing/backend/billing/items/create/page.tsx +110 -0
- package/src/modules/billing/backend/billing/items/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/items/page.tsx +272 -0
- package/src/modules/billing/backend/billing/runs/[id]/page.meta.ts +19 -0
- package/src/modules/billing/backend/billing/runs/[id]/page.tsx +404 -0
- package/src/modules/billing/backend/billing/runs/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/runs/page.tsx +426 -0
- package/src/modules/billing/backend/billing/usage/create/page.meta.ts +23 -0
- package/src/modules/billing/backend/billing/usage/create/page.tsx +120 -0
- package/src/modules/billing/ce.ts +74 -0
- package/src/modules/billing/cli.ts +89 -0
- package/src/modules/billing/commands/accounts.ts +205 -0
- package/src/modules/billing/commands/index.ts +13 -0
- package/src/modules/billing/commands/invoices.ts +542 -0
- package/src/modules/billing/commands/items.ts +438 -0
- package/src/modules/billing/commands/runs.ts +166 -0
- package/src/modules/billing/commands/usage.ts +118 -0
- package/src/modules/billing/components/AccountForm.tsx +517 -0
- package/src/modules/billing/components/DetailFields.tsx +60 -0
- package/src/modules/billing/components/ItemForm.tsx +558 -0
- package/src/modules/billing/components/LineFormDialog.tsx +253 -0
- package/src/modules/billing/components/UsageForm.tsx +326 -0
- package/src/modules/billing/data/entities.ts +533 -0
- package/src/modules/billing/data/entityIds.ts +25 -0
- package/src/modules/billing/data/validators.ts +562 -0
- package/src/modules/billing/di.ts +14 -0
- package/src/modules/billing/events.ts +47 -0
- package/src/modules/billing/i18n/en.json +336 -0
- package/src/modules/billing/i18n/pl.json +336 -0
- package/src/modules/billing/index.ts +22 -0
- package/src/modules/billing/lib/billPeriod.ts +181 -0
- package/src/modules/billing/lib/billRunEngine.ts +416 -0
- package/src/modules/billing/lib/billRunLabel.ts +14 -0
- package/src/modules/billing/lib/idempotency.ts +46 -0
- package/src/modules/billing/lib/invoiceStatus.ts +61 -0
- package/src/modules/billing/lib/invoiceWriter.ts +260 -0
- package/src/modules/billing/lib/itemSelector.ts +154 -0
- package/src/modules/billing/lib/seeds/demo.ts +199 -0
- package/src/modules/billing/lib/tenantLock.ts +87 -0
- package/src/modules/billing/lib/usageRater.ts +250 -0
- package/src/modules/billing/lib/usageRunner.ts +189 -0
- package/src/modules/billing/migrations/Migration20260519000000_billing.ts +175 -0
- package/src/modules/billing/setup.ts +252 -0
- package/src/modules/billing/subscribers/payment-captured-to-paid.ts +89 -0
- package/src/modules/billing/translations.ts +9 -0
- package/src/modules/billing/workers/reap-stale-bill-runs.ts +57 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import { OptionalProps } from '@mikro-orm/core'
|
|
2
|
+
import { Entity, Index, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy'
|
|
3
|
+
|
|
4
|
+
// ─── BillingAccount ──────────────────────────────────────────────
|
|
5
|
+
//
|
|
6
|
+
// A container per customer (typically one customer = one account, but the
|
|
7
|
+
// engine does not enforce uniqueness — multi-account-per-customer is a
|
|
8
|
+
// legitimate model for B2B with separate budgets / currencies / branches).
|
|
9
|
+
//
|
|
10
|
+
// The `(organization_id, tenant_id, next_bill_date)` partial index is the
|
|
11
|
+
// Bill Run hot path — every nightly run filters by tenant + org and orders
|
|
12
|
+
// by next_bill_date. Multi-org tenants share `tenant_id` across orgs, so a
|
|
13
|
+
// `(tenant_id, next_bill_date)`-only index would scan every other org's
|
|
14
|
+
// rows for each tenant — fine at 1 org / tenant, painful at 50.
|
|
15
|
+
|
|
16
|
+
@Entity({ tableName: 'billing_accounts' })
|
|
17
|
+
@Index({
|
|
18
|
+
name: 'billing_accounts_next_bill_date_idx',
|
|
19
|
+
properties: ['organizationId', 'tenantId', 'nextBillDate'],
|
|
20
|
+
expression:
|
|
21
|
+
'CREATE INDEX "billing_accounts_next_bill_date_idx" ON "billing_accounts" ("organization_id", "tenant_id", "next_bill_date") WHERE "deleted_at" IS NULL',
|
|
22
|
+
})
|
|
23
|
+
@Index({
|
|
24
|
+
name: 'billing_accounts_customer_idx',
|
|
25
|
+
properties: ['organizationId', 'tenantId', 'customerId'],
|
|
26
|
+
})
|
|
27
|
+
@Index({
|
|
28
|
+
name: 'billing_accounts_sort_idx',
|
|
29
|
+
properties: ['organizationId', 'tenantId', 'deletedAt', 'createdAt'],
|
|
30
|
+
})
|
|
31
|
+
export class BillingAccount {
|
|
32
|
+
[OptionalProps]?:
|
|
33
|
+
| 'taxId'
|
|
34
|
+
| 'lastBillDate'
|
|
35
|
+
| 'isActive'
|
|
36
|
+
| 'createdAt'
|
|
37
|
+
| 'updatedAt'
|
|
38
|
+
| 'deletedAt'
|
|
39
|
+
|
|
40
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
41
|
+
id!: string
|
|
42
|
+
|
|
43
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
44
|
+
organizationId!: string
|
|
45
|
+
|
|
46
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
47
|
+
tenantId!: string
|
|
48
|
+
|
|
49
|
+
// String FK to customers.person.id (or any external customer system).
|
|
50
|
+
// Kept as text so the package stays installable without the customers
|
|
51
|
+
// module — see SPEC-001 cross-package data rule.
|
|
52
|
+
@Property({ name: 'customer_id', type: 'text' })
|
|
53
|
+
customerId!: string
|
|
54
|
+
|
|
55
|
+
@Property({ type: 'text' })
|
|
56
|
+
name!: string
|
|
57
|
+
|
|
58
|
+
// ISO 4217 currency code. An account is single-currency by design —
|
|
59
|
+
// multi-currency = multiple accounts.
|
|
60
|
+
@Property({ name: 'currency_code', type: 'text' })
|
|
61
|
+
currencyCode!: string
|
|
62
|
+
|
|
63
|
+
// 'monthly' | 'quarterly' | 'annually' | 'weekly'. Validated at API ingress.
|
|
64
|
+
@Property({ name: 'bill_cycle', type: 'text' })
|
|
65
|
+
billCycle!: string
|
|
66
|
+
|
|
67
|
+
// Day-of-month for monthly/quarterly/annually (1–28), or weekday code
|
|
68
|
+
// ('mon'…'sun') for weekly. Text so both shapes coexist.
|
|
69
|
+
@Property({ name: 'bill_cycle_anchor', type: 'text' })
|
|
70
|
+
billCycleAnchor!: string
|
|
71
|
+
|
|
72
|
+
@Property({ name: 'invoice_email', type: 'text' })
|
|
73
|
+
invoiceEmail!: string
|
|
74
|
+
|
|
75
|
+
// ISO 639-1 (`en`, `pl`, …). Used by mailer / template renderer.
|
|
76
|
+
@Property({ name: 'invoice_language', type: 'text' })
|
|
77
|
+
invoiceLanguage!: string
|
|
78
|
+
|
|
79
|
+
// NIP / VATIN / TaxID. Nullable for individuals without a registered tax ID.
|
|
80
|
+
@Property({ name: 'tax_id', type: 'text', nullable: true })
|
|
81
|
+
taxId?: string | null
|
|
82
|
+
|
|
83
|
+
// { line1, line2?, city, postal_code, country } — JSONB because address
|
|
84
|
+
// shape differs across countries and we never query by address fields.
|
|
85
|
+
@Property({ name: 'invoice_address', type: 'jsonb' })
|
|
86
|
+
invoiceAddress!: Record<string, unknown>
|
|
87
|
+
|
|
88
|
+
// Anchor of the NEXT bill period to be processed (advanced by Bill Run
|
|
89
|
+
// on success). Date-only — calendar-aware cycle arithmetic.
|
|
90
|
+
@Property({ name: 'next_bill_date', type: 'date', columnType: 'date' })
|
|
91
|
+
nextBillDate!: Date
|
|
92
|
+
|
|
93
|
+
// Anchor of the most recently billed period; null for fresh accounts.
|
|
94
|
+
@Property({ name: 'last_bill_date', type: 'date', columnType: 'date', nullable: true })
|
|
95
|
+
lastBillDate?: Date | null
|
|
96
|
+
|
|
97
|
+
@Property({ name: 'is_active', type: 'boolean', default: true })
|
|
98
|
+
isActive: boolean = true
|
|
99
|
+
|
|
100
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
101
|
+
createdAt: Date = new Date()
|
|
102
|
+
|
|
103
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
104
|
+
updatedAt: Date = new Date()
|
|
105
|
+
|
|
106
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
107
|
+
deletedAt?: Date | null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─── BillingItem ─────────────────────────────────────────────────
|
|
111
|
+
//
|
|
112
|
+
// A single line that will appear on an invoice. Three flavours:
|
|
113
|
+
// `one_time` / `recurring` / `usage` — see rate_json shape per type in
|
|
114
|
+
// data/validators.ts.
|
|
115
|
+
//
|
|
116
|
+
// `(tenant_id, bill_account_id, source_ref)` is unique for idempotency.
|
|
117
|
+
// Postgres treats NULLs as distinct, so items without a source_ref do not
|
|
118
|
+
// conflict — that is intentional (CSV imports / manual entries).
|
|
119
|
+
|
|
120
|
+
@Entity({ tableName: 'billing_items' })
|
|
121
|
+
@Unique({
|
|
122
|
+
name: 'billing_items_source_ref_unique',
|
|
123
|
+
properties: ['tenantId', 'billAccountId', 'sourceRef'],
|
|
124
|
+
})
|
|
125
|
+
@Index({
|
|
126
|
+
name: 'billing_items_account_type_idx',
|
|
127
|
+
properties: ['billAccountId', 'type'],
|
|
128
|
+
})
|
|
129
|
+
@Index({
|
|
130
|
+
name: 'billing_items_account_active_idx',
|
|
131
|
+
properties: ['billAccountId', 'type', 'isActive'],
|
|
132
|
+
expression:
|
|
133
|
+
'CREATE INDEX "billing_items_account_active_idx" ON "billing_items" ("bill_account_id", "type", "is_active") WHERE "deleted_at" IS NULL',
|
|
134
|
+
})
|
|
135
|
+
@Index({
|
|
136
|
+
name: 'billing_items_subscription_idx',
|
|
137
|
+
properties: ['organizationId', 'tenantId', 'subscriptionId'],
|
|
138
|
+
})
|
|
139
|
+
export class BillingItem {
|
|
140
|
+
[OptionalProps]?:
|
|
141
|
+
| 'billEndDate'
|
|
142
|
+
| 'uomCode'
|
|
143
|
+
| 'subscriptionId'
|
|
144
|
+
| 'subscriptionItemId'
|
|
145
|
+
| 'sourceRef'
|
|
146
|
+
| 'currencyMismatch'
|
|
147
|
+
| 'billedToDate'
|
|
148
|
+
| 'isActive'
|
|
149
|
+
| 'createdAt'
|
|
150
|
+
| 'updatedAt'
|
|
151
|
+
| 'deletedAt'
|
|
152
|
+
|
|
153
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
154
|
+
id!: string
|
|
155
|
+
|
|
156
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
157
|
+
organizationId!: string
|
|
158
|
+
|
|
159
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
160
|
+
tenantId!: string
|
|
161
|
+
|
|
162
|
+
// FK string to BillingAccount.id (intra-module; no ORM relation kept to
|
|
163
|
+
// avoid coupling — we resolve at runtime).
|
|
164
|
+
@Property({ name: 'bill_account_id', type: 'uuid' })
|
|
165
|
+
billAccountId!: string
|
|
166
|
+
|
|
167
|
+
// 'one_time' | 'recurring' | 'usage'.
|
|
168
|
+
@Property({ type: 'text' })
|
|
169
|
+
type!: string
|
|
170
|
+
|
|
171
|
+
@Property({ name: 'bill_start_date', type: 'date', columnType: 'date' })
|
|
172
|
+
billStartDate!: Date
|
|
173
|
+
|
|
174
|
+
// Null = open-ended (typical for ongoing recurring items).
|
|
175
|
+
@Property({ name: 'bill_end_date', type: 'date', columnType: 'date', nullable: true })
|
|
176
|
+
billEndDate?: Date | null
|
|
177
|
+
|
|
178
|
+
@Property({ type: 'text' })
|
|
179
|
+
description!: string
|
|
180
|
+
|
|
181
|
+
// Pricing definition. Shape validated by Zod per `type`; see
|
|
182
|
+
// data/validators.ts.
|
|
183
|
+
@Property({ name: 'rate_json', type: 'jsonb' })
|
|
184
|
+
rateJson!: Record<string, unknown>
|
|
185
|
+
|
|
186
|
+
// Required for type='usage' (must match a Usage record's uom_code
|
|
187
|
+
// exactly — no conversion). Ignored for other types.
|
|
188
|
+
@Property({ name: 'uom_code', type: 'text', nullable: true })
|
|
189
|
+
uomCode?: string | null
|
|
190
|
+
|
|
191
|
+
// String FK to upstream subscription (e.g. CPQ's
|
|
192
|
+
// cpq_inventory_subscriptions.id). Null for CSV / manual / non-subscription
|
|
193
|
+
// contexts.
|
|
194
|
+
@Property({ name: 'subscription_id', type: 'text', nullable: true })
|
|
195
|
+
subscriptionId?: string | null
|
|
196
|
+
|
|
197
|
+
@Property({ name: 'subscription_item_id', type: 'text', nullable: true })
|
|
198
|
+
subscriptionItemId?: string | null
|
|
199
|
+
|
|
200
|
+
// Idempotency key — duplicate POST returns the existing row.
|
|
201
|
+
@Property({ name: 'source_ref', type: 'text', nullable: true })
|
|
202
|
+
sourceRef?: string | null
|
|
203
|
+
|
|
204
|
+
// Set when the integrator POSTed a value in a different currency than the
|
|
205
|
+
// account's currency_code. Operator sees a warning on the draft.
|
|
206
|
+
@Property({ name: 'currency_mismatch', type: 'boolean', default: false })
|
|
207
|
+
currencyMismatch: boolean = false
|
|
208
|
+
|
|
209
|
+
// Anti-double-bill guard at the item level — see spec
|
|
210
|
+
// "Why two anti-duplicate guards".
|
|
211
|
+
@Property({ name: 'billed_to_date', type: 'date', columnType: 'date', nullable: true })
|
|
212
|
+
billedToDate?: Date | null
|
|
213
|
+
|
|
214
|
+
@Property({ name: 'is_active', type: 'boolean', default: true })
|
|
215
|
+
isActive: boolean = true
|
|
216
|
+
|
|
217
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
218
|
+
createdAt: Date = new Date()
|
|
219
|
+
|
|
220
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
221
|
+
updatedAt: Date = new Date()
|
|
222
|
+
|
|
223
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
224
|
+
deletedAt?: Date | null
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ─── BillingAccountUsage ─────────────────────────────────────────
|
|
228
|
+
//
|
|
229
|
+
// Bucket for pre-aggregated usage uploaded by external systems. The
|
|
230
|
+
// selection index `(bill_account_id, uom_code, rated_in_bill_run_id,
|
|
231
|
+
// period_end)` matches the Bill Run "find unrated records" predicate
|
|
232
|
+
// exactly — keep it in sync if that predicate changes.
|
|
233
|
+
|
|
234
|
+
@Entity({ tableName: 'billing_account_usage' })
|
|
235
|
+
@Unique({
|
|
236
|
+
name: 'billing_account_usage_source_ref_unique',
|
|
237
|
+
properties: ['tenantId', 'billAccountId', 'sourceRef'],
|
|
238
|
+
})
|
|
239
|
+
@Index({
|
|
240
|
+
name: 'billing_account_usage_rating_idx',
|
|
241
|
+
properties: ['billAccountId', 'uomCode', 'ratedInBillRunId', 'periodEnd'],
|
|
242
|
+
})
|
|
243
|
+
export class BillingAccountUsage {
|
|
244
|
+
[OptionalProps]?:
|
|
245
|
+
| 'lineDescription'
|
|
246
|
+
| 'sourceRef'
|
|
247
|
+
| 'ratedInBillRunId'
|
|
248
|
+
| 'isActive'
|
|
249
|
+
| 'createdAt'
|
|
250
|
+
| 'updatedAt'
|
|
251
|
+
| 'deletedAt'
|
|
252
|
+
|
|
253
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
254
|
+
id!: string
|
|
255
|
+
|
|
256
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
257
|
+
organizationId!: string
|
|
258
|
+
|
|
259
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
260
|
+
tenantId!: string
|
|
261
|
+
|
|
262
|
+
@Property({ name: 'bill_account_id', type: 'uuid' })
|
|
263
|
+
billAccountId!: string
|
|
264
|
+
|
|
265
|
+
// Must match the `uom_code` on a corresponding `type='usage'` Billing Item.
|
|
266
|
+
@Property({ name: 'uom_code', type: 'text' })
|
|
267
|
+
uomCode!: string
|
|
268
|
+
|
|
269
|
+
// numeric(18,4) → string in app code (standard MikroORM numeric handling).
|
|
270
|
+
@Property({ type: 'numeric', columnType: 'numeric(18, 4)' })
|
|
271
|
+
quantity!: string
|
|
272
|
+
|
|
273
|
+
@Property({ name: 'period_start', type: Date })
|
|
274
|
+
periodStart!: Date
|
|
275
|
+
|
|
276
|
+
@Property({ name: 'period_end', type: Date })
|
|
277
|
+
periodEnd!: Date
|
|
278
|
+
|
|
279
|
+
// Optional per-record description. When set, Bill Run emits one invoice
|
|
280
|
+
// line per Usage record with this description. When null, the run
|
|
281
|
+
// aggregates all matching Usage records into a single line using the
|
|
282
|
+
// Billing Item's `description`.
|
|
283
|
+
@Property({ name: 'line_description', type: 'text', nullable: true })
|
|
284
|
+
lineDescription?: string | null
|
|
285
|
+
|
|
286
|
+
@Property({ name: 'source_ref', type: 'text', nullable: true })
|
|
287
|
+
sourceRef?: string | null
|
|
288
|
+
|
|
289
|
+
// Set when consumed; prevents double-rating on retry.
|
|
290
|
+
@Property({ name: 'rated_in_bill_run_id', type: 'uuid', nullable: true })
|
|
291
|
+
ratedInBillRunId?: string | null
|
|
292
|
+
|
|
293
|
+
@Property({ name: 'is_active', type: 'boolean', default: true })
|
|
294
|
+
isActive: boolean = true
|
|
295
|
+
|
|
296
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
297
|
+
createdAt: Date = new Date()
|
|
298
|
+
|
|
299
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
300
|
+
updatedAt: Date = new Date()
|
|
301
|
+
|
|
302
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
303
|
+
deletedAt?: Date | null
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ─── BillRun ─────────────────────────────────────────────────────
|
|
307
|
+
//
|
|
308
|
+
// A single execution of the engine — one row per trigger (schedule or
|
|
309
|
+
// manual). The concurrency lock is a Postgres transaction-scoped advisory
|
|
310
|
+
// lock keyed by hash(tenant_id || 'billing-run'); it is NOT stored on this
|
|
311
|
+
// row. Crash recovery for zombie `status='running'` rows is handled by
|
|
312
|
+
// workers/reap-stale-bill-runs.ts.
|
|
313
|
+
|
|
314
|
+
@Entity({ tableName: 'billing_runs' })
|
|
315
|
+
@Index({
|
|
316
|
+
name: 'billing_runs_tenant_status_idx',
|
|
317
|
+
properties: ['tenantId', 'status', 'startedAt'],
|
|
318
|
+
})
|
|
319
|
+
@Index({
|
|
320
|
+
name: 'billing_runs_parent_idx',
|
|
321
|
+
properties: ['parentRunId'],
|
|
322
|
+
})
|
|
323
|
+
export class BillRun {
|
|
324
|
+
[OptionalProps]?:
|
|
325
|
+
| 'parentRunId'
|
|
326
|
+
| 'dryRun'
|
|
327
|
+
| 'testMode'
|
|
328
|
+
| 'catchUp'
|
|
329
|
+
| 'scopedAccountIds'
|
|
330
|
+
| 'startedAt'
|
|
331
|
+
| 'finishedAt'
|
|
332
|
+
| 'summary'
|
|
333
|
+
| 'isActive'
|
|
334
|
+
| 'createdAt'
|
|
335
|
+
| 'updatedAt'
|
|
336
|
+
| 'deletedAt'
|
|
337
|
+
|
|
338
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
339
|
+
id!: string
|
|
340
|
+
|
|
341
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
342
|
+
organizationId!: string
|
|
343
|
+
|
|
344
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
345
|
+
tenantId!: string
|
|
346
|
+
|
|
347
|
+
// 'schedule' | 'manual'.
|
|
348
|
+
@Property({ name: 'triggered_by', type: 'text' })
|
|
349
|
+
triggeredBy!: string
|
|
350
|
+
|
|
351
|
+
// Set when this run is a "retry failed" of a previous run.
|
|
352
|
+
@Property({ name: 'parent_run_id', type: 'uuid', nullable: true })
|
|
353
|
+
parentRunId?: string | null
|
|
354
|
+
|
|
355
|
+
// True → compute everything but persist nothing (preview).
|
|
356
|
+
@Property({ name: 'dry_run', type: 'boolean', default: false })
|
|
357
|
+
dryRun: boolean = false
|
|
358
|
+
|
|
359
|
+
// True → persist drafts flagged as test, but do not advance state.
|
|
360
|
+
@Property({ name: 'test_mode', type: 'boolean', default: false })
|
|
361
|
+
testMode: boolean = false
|
|
362
|
+
|
|
363
|
+
// True → loop missed cycles per account in one run (manual only).
|
|
364
|
+
@Property({ name: 'catch_up', type: 'boolean', default: false })
|
|
365
|
+
catchUp: boolean = false
|
|
366
|
+
|
|
367
|
+
// Optional — run processes only these accounts when set. JSONB
|
|
368
|
+
// (Postgres arrays would work too, but jsonb keeps tooling uniform
|
|
369
|
+
// across this package).
|
|
370
|
+
@Property({ name: 'scoped_account_ids', type: 'jsonb', nullable: true })
|
|
371
|
+
scopedAccountIds?: string[] | null
|
|
372
|
+
|
|
373
|
+
// The "today" the run uses to evaluate `next_bill_date <= as_of_date`.
|
|
374
|
+
@Property({ name: 'as_of_date', type: 'date', columnType: 'date' })
|
|
375
|
+
asOfDate!: Date
|
|
376
|
+
|
|
377
|
+
@Property({ name: 'started_at', type: Date, nullable: true })
|
|
378
|
+
startedAt?: Date | null
|
|
379
|
+
|
|
380
|
+
@Property({ name: 'finished_at', type: Date, nullable: true })
|
|
381
|
+
finishedAt?: Date | null
|
|
382
|
+
|
|
383
|
+
// 'running' | 'completed' | 'partial_failure' | 'failed'.
|
|
384
|
+
@Property({ type: 'text' })
|
|
385
|
+
status!: string
|
|
386
|
+
|
|
387
|
+
// { accounts_processed, drafts_created, drafts_skipped_existing,
|
|
388
|
+
// accounts_failed, accounts_with_warnings, usage_records_rated }
|
|
389
|
+
@Property({ type: 'jsonb', nullable: true })
|
|
390
|
+
summary?: Record<string, unknown> | null
|
|
391
|
+
|
|
392
|
+
@Property({ name: 'is_active', type: 'boolean', default: true })
|
|
393
|
+
isActive: boolean = true
|
|
394
|
+
|
|
395
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
396
|
+
createdAt: Date = new Date()
|
|
397
|
+
|
|
398
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
399
|
+
updatedAt: Date = new Date()
|
|
400
|
+
|
|
401
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
402
|
+
deletedAt?: Date | null
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ─── BillRunOutcome ──────────────────────────────────────────────
|
|
406
|
+
//
|
|
407
|
+
// Per-account outcome of a Bill Run. Stored in its own table (not as a
|
|
408
|
+
// JSONB array on BillRun.summary) so operators can query failures across
|
|
409
|
+
// runs without jsonb_array_elements gymnastics. See spec rationale.
|
|
410
|
+
|
|
411
|
+
@Entity({ tableName: 'billing_run_outcomes' })
|
|
412
|
+
@Index({
|
|
413
|
+
name: 'billing_run_outcomes_run_status_idx',
|
|
414
|
+
properties: ['billRunId', 'status'],
|
|
415
|
+
})
|
|
416
|
+
@Index({
|
|
417
|
+
name: 'billing_run_outcomes_account_history_idx',
|
|
418
|
+
properties: ['organizationId', 'tenantId', 'billAccountId', 'createdAt'],
|
|
419
|
+
})
|
|
420
|
+
export class BillRunOutcome {
|
|
421
|
+
[OptionalProps]?:
|
|
422
|
+
| 'errorMessage'
|
|
423
|
+
| 'warnings'
|
|
424
|
+
| 'draftInvoiceId'
|
|
425
|
+
| 'isActive'
|
|
426
|
+
| 'createdAt'
|
|
427
|
+
| 'updatedAt'
|
|
428
|
+
| 'deletedAt'
|
|
429
|
+
|
|
430
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
431
|
+
id!: string
|
|
432
|
+
|
|
433
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
434
|
+
organizationId!: string
|
|
435
|
+
|
|
436
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
437
|
+
tenantId!: string
|
|
438
|
+
|
|
439
|
+
@Property({ name: 'bill_run_id', type: 'uuid' })
|
|
440
|
+
billRunId!: string
|
|
441
|
+
|
|
442
|
+
@Property({ name: 'bill_account_id', type: 'uuid' })
|
|
443
|
+
billAccountId!: string
|
|
444
|
+
|
|
445
|
+
// 'success' | 'success_with_warnings' | 'skipped_existing_draft' | 'failed'.
|
|
446
|
+
@Property({ type: 'text' })
|
|
447
|
+
status!: string
|
|
448
|
+
|
|
449
|
+
@Property({ name: 'error_message', type: 'text', nullable: true })
|
|
450
|
+
errorMessage?: string | null
|
|
451
|
+
|
|
452
|
+
// E.g. `{ unmatched_usage_uoms: ["api_request"] }`.
|
|
453
|
+
@Property({ type: 'jsonb', nullable: true })
|
|
454
|
+
warnings?: Record<string, unknown> | null
|
|
455
|
+
|
|
456
|
+
// FK string to the core/sales invoice (drafted by the run). Null when
|
|
457
|
+
// the run skipped (status='skipped_existing_draft' or 'failed').
|
|
458
|
+
@Property({ name: 'draft_invoice_id', type: 'uuid', nullable: true })
|
|
459
|
+
draftInvoiceId?: string | null
|
|
460
|
+
|
|
461
|
+
@Property({ name: 'is_active', type: 'boolean', default: true })
|
|
462
|
+
isActive: boolean = true
|
|
463
|
+
|
|
464
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
465
|
+
createdAt: Date = new Date()
|
|
466
|
+
|
|
467
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
468
|
+
updatedAt: Date = new Date()
|
|
469
|
+
|
|
470
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
471
|
+
deletedAt?: Date | null
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ─── DraftInvoiceEdit (audit) ────────────────────────────────────
|
|
475
|
+
//
|
|
476
|
+
// Append-only audit of every operator edit on a draft invoice line.
|
|
477
|
+
// Pattern mirrors `cpq_subscription_change_logs` — minimal columns
|
|
478
|
+
// (no updated_at / is_active), append-only, soft-delete reserved for
|
|
479
|
+
// compliance erasure only.
|
|
480
|
+
|
|
481
|
+
@Entity({ tableName: 'billing_draft_invoice_edits' })
|
|
482
|
+
@Index({
|
|
483
|
+
name: 'billing_draft_invoice_edits_invoice_idx',
|
|
484
|
+
properties: ['organizationId', 'tenantId', 'invoiceId', 'createdAt'],
|
|
485
|
+
})
|
|
486
|
+
@Index({
|
|
487
|
+
name: 'billing_draft_invoice_edits_user_idx',
|
|
488
|
+
properties: ['organizationId', 'tenantId', 'userId', 'createdAt'],
|
|
489
|
+
})
|
|
490
|
+
export class DraftInvoiceEdit {
|
|
491
|
+
[OptionalProps]?: 'invoiceLineId' | 'beforeJson' | 'afterJson' | 'createdAt' | 'deletedAt'
|
|
492
|
+
|
|
493
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
494
|
+
id!: string
|
|
495
|
+
|
|
496
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
497
|
+
organizationId!: string
|
|
498
|
+
|
|
499
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
500
|
+
tenantId!: string
|
|
501
|
+
|
|
502
|
+
// FK string to the core/sales invoice (no ORM relation across packages).
|
|
503
|
+
@Property({ name: 'invoice_id', type: 'uuid' })
|
|
504
|
+
invoiceId!: string
|
|
505
|
+
|
|
506
|
+
// Null for `line_added` / `line_removed` actions whose target line does
|
|
507
|
+
// not yet (or no longer) exist on the invoice.
|
|
508
|
+
@Property({ name: 'invoice_line_id', type: 'uuid', nullable: true })
|
|
509
|
+
invoiceLineId?: string | null
|
|
510
|
+
|
|
511
|
+
@Property({ name: 'user_id', type: 'uuid' })
|
|
512
|
+
userId!: string
|
|
513
|
+
|
|
514
|
+
// 'line_added' | 'line_removed' | 'line_edited'.
|
|
515
|
+
@Property({ type: 'text' })
|
|
516
|
+
action!: string
|
|
517
|
+
|
|
518
|
+
// Snapshot before the edit. Null for `line_added` (no prior state).
|
|
519
|
+
@Property({ name: 'before_json', type: 'jsonb', nullable: true })
|
|
520
|
+
beforeJson?: Record<string, unknown> | null
|
|
521
|
+
|
|
522
|
+
// Snapshot after the edit. Null for `line_removed` (no resulting state).
|
|
523
|
+
@Property({ name: 'after_json', type: 'jsonb', nullable: true })
|
|
524
|
+
afterJson?: Record<string, unknown> | null
|
|
525
|
+
|
|
526
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
527
|
+
createdAt: Date = new Date()
|
|
528
|
+
|
|
529
|
+
// Reserved for compliance erasure only; runtime never deletes audit rows
|
|
530
|
+
// through normal flows.
|
|
531
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
532
|
+
deletedAt?: Date | null
|
|
533
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical entity IDs for the billing module.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors what `E.billing.<entity>` would be in
|
|
5
|
+
* `.mercato/generated/entities.ids.generated.ts` if the standalone-app
|
|
6
|
+
* CLI properly discovered `@dainamite/*` workspace entities (see the
|
|
7
|
+
* Phase 0 deviation note in `xd-249-billing-spec.md`). Until the CLI
|
|
8
|
+
* gap is closed upstream, this is the single source of truth — every
|
|
9
|
+
* place that previously would have referenced `E.billing.*` (CRUD
|
|
10
|
+
* factory `entityId`, indexer `entityType`, response enricher
|
|
11
|
+
* `targetEntity`, etc.) imports from here instead.
|
|
12
|
+
*
|
|
13
|
+
* When the CLI is fixed, this file can be replaced by `import { E }
|
|
14
|
+
* from '#generated/entities.ids.generated'` in one PR.
|
|
15
|
+
*/
|
|
16
|
+
export const billingEntityIds = {
|
|
17
|
+
account: 'billing:billing_account',
|
|
18
|
+
item: 'billing:billing_item',
|
|
19
|
+
usage: 'billing:billing_account_usage',
|
|
20
|
+
run: 'billing:billing_run',
|
|
21
|
+
runOutcome: 'billing:billing_run_outcome',
|
|
22
|
+
draftInvoiceEdit: 'billing:billing_draft_invoice_edit',
|
|
23
|
+
} as const
|
|
24
|
+
|
|
25
|
+
export type BillingEntityId = (typeof billingEntityIds)[keyof typeof billingEntityIds]
|