@dainamite/billing 0.17.0 → 0.18.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 +81 -19
- package/dist/modules/billing/acl.js +9 -1
- package/dist/modules/billing/acl.js.map +2 -2
- package/dist/modules/billing/api/accounts/[id]/balance/route.js +62 -0
- package/dist/modules/billing/api/accounts/[id]/balance/route.js.map +7 -0
- package/dist/modules/billing/api/accounts/[id]/credit-status/route.js +56 -0
- package/dist/modules/billing/api/accounts/[id]/credit-status/route.js.map +7 -0
- package/dist/modules/billing/api/accounts/[id]/topups/route.js +36 -0
- package/dist/modules/billing/api/accounts/[id]/topups/route.js.map +7 -0
- package/dist/modules/billing/api/accounts/route.js +7 -0
- package/dist/modules/billing/api/accounts/route.js.map +2 -2
- package/dist/modules/billing/api/export/account/[id]/route.js +48 -3
- package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
- package/dist/modules/billing/api/statements/generate/route.js +68 -0
- package/dist/modules/billing/api/statements/generate/route.js.map +7 -0
- package/dist/modules/billing/api/statements/route.js +110 -0
- package/dist/modules/billing/api/statements/route.js.map +7 -0
- package/dist/modules/billing/api/topups/route.js +139 -0
- package/dist/modules/billing/api/topups/route.js.map +7 -0
- package/dist/modules/billing/api/transactions/adjust/route.js +71 -0
- package/dist/modules/billing/api/transactions/adjust/route.js.map +7 -0
- package/dist/modules/billing/api/transactions/route.js +109 -0
- package/dist/modules/billing/api/transactions/route.js.map +7 -0
- package/dist/modules/billing/api/usage/route.js +30 -6
- package/dist/modules/billing/api/usage/route.js.map +2 -2
- package/dist/modules/billing/backend/billing/accounts/[id]/page.js +16 -1
- package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/accounts/create/page.js +8 -1
- package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/accounts/page.js +26 -1
- package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/statements/[id]/page.js +129 -0
- package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/statements/[id]/page.meta.js +21 -0
- package/dist/modules/billing/backend/billing/statements/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/statements/page.js +152 -0
- package/dist/modules/billing/backend/billing/statements/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/statements/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/statements/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/topups/[id]/page.js +90 -0
- package/dist/modules/billing/backend/billing/topups/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/topups/[id]/page.meta.js +21 -0
- package/dist/modules/billing/backend/billing/topups/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/topups/page.js +147 -0
- package/dist/modules/billing/backend/billing/topups/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/topups/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/topups/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/transactions/[id]/page.js +114 -0
- package/dist/modules/billing/backend/billing/transactions/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/transactions/[id]/page.meta.js +21 -0
- package/dist/modules/billing/backend/billing/transactions/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/transactions/page.js +149 -0
- package/dist/modules/billing/backend/billing/transactions/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/transactions/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/transactions/page.meta.js.map +7 -0
- package/dist/modules/billing/commands/accounts.js +26 -1
- package/dist/modules/billing/commands/accounts.js.map +2 -2
- package/dist/modules/billing/commands/index.js +3 -0
- package/dist/modules/billing/commands/index.js.map +2 -2
- package/dist/modules/billing/commands/invoices.js +21 -1
- package/dist/modules/billing/commands/invoices.js.map +2 -2
- package/dist/modules/billing/commands/statements.js +74 -0
- package/dist/modules/billing/commands/statements.js.map +7 -0
- package/dist/modules/billing/commands/topups.js +139 -0
- package/dist/modules/billing/commands/topups.js.map +7 -0
- package/dist/modules/billing/commands/transactions.js +119 -0
- package/dist/modules/billing/commands/transactions.js.map +7 -0
- package/dist/modules/billing/commands/usage.js +72 -0
- package/dist/modules/billing/commands/usage.js.map +2 -2
- package/dist/modules/billing/components/AccountForm.js +53 -1
- package/dist/modules/billing/components/AccountForm.js.map +2 -2
- package/dist/modules/billing/components/PrepaidPanel.js +314 -0
- package/dist/modules/billing/components/PrepaidPanel.js.map +7 -0
- package/dist/modules/billing/components/accountNames.js +20 -0
- package/dist/modules/billing/components/accountNames.js.map +7 -0
- package/dist/modules/billing/data/entities.js +290 -0
- package/dist/modules/billing/data/entities.js.map +2 -2
- package/dist/modules/billing/data/entityIds.js +6 -1
- package/dist/modules/billing/data/entityIds.js.map +2 -2
- package/dist/modules/billing/data/validators.js +107 -2
- package/dist/modules/billing/data/validators.js.map +2 -2
- package/dist/modules/billing/events.js +52 -0
- package/dist/modules/billing/events.js.map +2 -2
- package/dist/modules/billing/i18n/en.json +112 -4
- package/dist/modules/billing/i18n/pl.json +112 -4
- package/dist/modules/billing/index.js +1 -1
- package/dist/modules/billing/index.js.map +2 -2
- package/dist/modules/billing/lib/accountStatus.js +106 -0
- package/dist/modules/billing/lib/accountStatus.js.map +7 -0
- package/dist/modules/billing/lib/balanceEvents.js +48 -0
- package/dist/modules/billing/lib/balanceEvents.js.map +7 -0
- package/dist/modules/billing/lib/balanceLedger.js +136 -0
- package/dist/modules/billing/lib/balanceLedger.js.map +7 -0
- package/dist/modules/billing/lib/balanceStatus.js +29 -0
- package/dist/modules/billing/lib/balanceStatus.js.map +7 -0
- package/dist/modules/billing/lib/billRunEngine.js +40 -2
- package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
- package/dist/modules/billing/lib/creditStatus.js +65 -0
- package/dist/modules/billing/lib/creditStatus.js.map +7 -0
- package/dist/modules/billing/lib/money.js +47 -0
- package/dist/modules/billing/lib/money.js.map +7 -0
- package/dist/modules/billing/lib/prepaidConfig.js +45 -0
- package/dist/modules/billing/lib/prepaidConfig.js.map +7 -0
- package/dist/modules/billing/lib/prepaidConsume.js +214 -0
- package/dist/modules/billing/lib/prepaidConsume.js.map +7 -0
- package/dist/modules/billing/lib/prepaidPeriodClose.js +285 -0
- package/dist/modules/billing/lib/prepaidPeriodClose.js.map +7 -0
- package/dist/modules/billing/lib/topupCapture.js +62 -0
- package/dist/modules/billing/lib/topupCapture.js.map +7 -0
- package/dist/modules/billing/lib/topupReceipt.js +97 -0
- package/dist/modules/billing/lib/topupReceipt.js.map +7 -0
- package/dist/modules/billing/lib/topupTransition.js +19 -0
- package/dist/modules/billing/lib/topupTransition.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20260619000000_billing_prepaid.js +110 -0
- package/dist/modules/billing/migrations/Migration20260619000000_billing_prepaid.js.map +7 -0
- package/dist/modules/billing/setup.js +32 -1
- package/dist/modules/billing/setup.js.map +2 -2
- package/dist/modules/billing/subscribers/prepaid-topup-cancelled.js +26 -0
- package/dist/modules/billing/subscribers/prepaid-topup-cancelled.js.map +7 -0
- package/dist/modules/billing/subscribers/prepaid-topup-captured.js +87 -0
- package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +7 -0
- package/dist/modules/billing/subscribers/prepaid-topup-expired.js +26 -0
- package/dist/modules/billing/subscribers/prepaid-topup-expired.js.map +7 -0
- package/dist/modules/billing/subscribers/prepaid-topup-failed.js +26 -0
- package/dist/modules/billing/subscribers/prepaid-topup-failed.js.map +7 -0
- package/dist/modules/billing/workers/reconcile-prepaid-balances.js +50 -0
- package/dist/modules/billing/workers/reconcile-prepaid-balances.js.map +7 -0
- package/package.json +1 -1
- package/src/modules/billing/__tests__/commands/topups.test.ts +104 -0
- package/src/modules/billing/__tests__/commands/transactions.test.ts +84 -0
- package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +235 -0
- package/src/modules/billing/__tests__/lib/accountStatus.test.ts +93 -0
- package/src/modules/billing/__tests__/lib/balanceLedger.test.ts +125 -0
- package/src/modules/billing/__tests__/lib/balanceStatus.test.ts +65 -0
- package/src/modules/billing/__tests__/lib/creditStatus.test.ts +111 -0
- package/src/modules/billing/__tests__/lib/money.test.ts +56 -0
- package/src/modules/billing/__tests__/lib/prepaidConsume.test.ts +179 -0
- package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +232 -0
- package/src/modules/billing/__tests__/lib/topupCapture.test.ts +123 -0
- package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +84 -0
- package/src/modules/billing/__tests__/setup.test.ts +28 -2
- package/src/modules/billing/acl.ts +8 -0
- package/src/modules/billing/api/accounts/[id]/balance/route.ts +74 -0
- package/src/modules/billing/api/accounts/[id]/credit-status/route.ts +71 -0
- package/src/modules/billing/api/accounts/[id]/topups/route.ts +49 -0
- package/src/modules/billing/api/accounts/route.ts +7 -0
- package/src/modules/billing/api/export/account/[id]/route.ts +47 -2
- package/src/modules/billing/api/statements/generate/route.ts +78 -0
- package/src/modules/billing/api/statements/route.ts +117 -0
- package/src/modules/billing/api/topups/route.ts +154 -0
- package/src/modules/billing/api/transactions/adjust/route.ts +80 -0
- package/src/modules/billing/api/transactions/route.ts +116 -0
- package/src/modules/billing/api/usage/route.ts +36 -5
- package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +28 -0
- package/src/modules/billing/backend/billing/accounts/create/page.tsx +7 -0
- package/src/modules/billing/backend/billing/accounts/page.tsx +32 -1
- package/src/modules/billing/backend/billing/statements/[id]/page.meta.ts +19 -0
- package/src/modules/billing/backend/billing/statements/[id]/page.tsx +198 -0
- package/src/modules/billing/backend/billing/statements/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/statements/page.tsx +190 -0
- package/src/modules/billing/backend/billing/topups/[id]/page.meta.ts +19 -0
- package/src/modules/billing/backend/billing/topups/[id]/page.tsx +151 -0
- package/src/modules/billing/backend/billing/topups/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/topups/page.tsx +182 -0
- package/src/modules/billing/backend/billing/transactions/[id]/page.meta.ts +19 -0
- package/src/modules/billing/backend/billing/transactions/[id]/page.tsx +179 -0
- package/src/modules/billing/backend/billing/transactions/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/transactions/page.tsx +187 -0
- package/src/modules/billing/commands/accounts.ts +42 -1
- package/src/modules/billing/commands/index.ts +3 -0
- package/src/modules/billing/commands/invoices.ts +26 -2
- package/src/modules/billing/commands/statements.ts +101 -0
- package/src/modules/billing/commands/topups.ts +181 -0
- package/src/modules/billing/commands/transactions.ts +152 -0
- package/src/modules/billing/commands/usage.ts +114 -4
- package/src/modules/billing/components/AccountForm.tsx +66 -0
- package/src/modules/billing/components/PrepaidPanel.tsx +382 -0
- package/src/modules/billing/components/accountNames.ts +23 -0
- package/src/modules/billing/data/entities.ts +383 -0
- package/src/modules/billing/data/entityIds.ts +5 -0
- package/src/modules/billing/data/validators.ts +144 -0
- package/src/modules/billing/events.ts +52 -0
- package/src/modules/billing/i18n/en.json +112 -4
- package/src/modules/billing/i18n/pl.json +112 -4
- package/src/modules/billing/index.ts +1 -1
- package/src/modules/billing/lib/accountStatus.ts +155 -0
- package/src/modules/billing/lib/balanceEvents.ts +74 -0
- package/src/modules/billing/lib/balanceLedger.ts +258 -0
- package/src/modules/billing/lib/balanceStatus.ts +78 -0
- package/src/modules/billing/lib/billRunEngine.ts +66 -5
- package/src/modules/billing/lib/creditStatus.ts +153 -0
- package/src/modules/billing/lib/money.ts +73 -0
- package/src/modules/billing/lib/prepaidConfig.ts +58 -0
- package/src/modules/billing/lib/prepaidConsume.ts +299 -0
- package/src/modules/billing/lib/prepaidPeriodClose.ts +453 -0
- package/src/modules/billing/lib/topupCapture.ts +117 -0
- package/src/modules/billing/lib/topupReceipt.ts +153 -0
- package/src/modules/billing/lib/topupTransition.ts +31 -0
- package/src/modules/billing/migrations/Migration20260619000000_billing_prepaid.ts +131 -0
- package/src/modules/billing/setup.ts +45 -0
- package/src/modules/billing/subscribers/prepaid-topup-cancelled.ts +31 -0
- package/src/modules/billing/subscribers/prepaid-topup-captured.ts +120 -0
- package/src/modules/billing/subscribers/prepaid-topup-expired.ts +31 -0
- package/src/modules/billing/subscribers/prepaid-topup-failed.ts +32 -0
- package/src/modules/billing/workers/reconcile-prepaid-balances.ts +68 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"billing.accounts.actions.open": "Open",
|
|
3
|
+
"billing.accounts.columns.credit_limit": "Credit limit",
|
|
3
4
|
"billing.accounts.columns.currency": "Currency",
|
|
4
5
|
"billing.accounts.columns.customer": "Customer",
|
|
5
6
|
"billing.accounts.columns.cycle": "Cycle",
|
|
6
7
|
"billing.accounts.columns.last_bill": "Last bill",
|
|
8
|
+
"billing.accounts.columns.mode": "Mode",
|
|
7
9
|
"billing.accounts.columns.name": "Name",
|
|
8
10
|
"billing.accounts.columns.next_bill": "Next bill",
|
|
9
11
|
"billing.accounts.columns.status": "Status",
|
|
@@ -62,6 +64,8 @@
|
|
|
62
64
|
"billing.accounts.form.error.next_bill_date_required": "Next bill date is required",
|
|
63
65
|
"billing.accounts.form.invoice_email": "Invoice email",
|
|
64
66
|
"billing.accounts.form.invoice_language": "Invoice language (ISO 639-1)",
|
|
67
|
+
"billing.accounts.form.mode.locked": "Immutable — frozen at account create time.",
|
|
68
|
+
"billing.accounts.form.mode.title": "Billing mode & credit",
|
|
65
69
|
"billing.accounts.form.name": "Name",
|
|
66
70
|
"billing.accounts.form.name.placeholder": "Auto-filled from customer — override e.g. \"Acme — production\"",
|
|
67
71
|
"billing.accounts.form.next_bill_date": "Next bill date",
|
|
@@ -69,6 +73,7 @@
|
|
|
69
73
|
"billing.accounts.form.saving": "Saving…",
|
|
70
74
|
"billing.accounts.form.submit_hint": "to save,",
|
|
71
75
|
"billing.accounts.form.tax_id": "Tax ID (NIP / VATIN, optional)",
|
|
76
|
+
"billing.accounts.form.threshold.hint": "Blank = tenant default",
|
|
72
77
|
"billing.accounts.title": "Billing Accounts",
|
|
73
78
|
"billing.common.active": "Active",
|
|
74
79
|
"billing.common.cancel": "Cancel",
|
|
@@ -233,6 +238,37 @@
|
|
|
233
238
|
"billing.items.type.one_time": "One-time",
|
|
234
239
|
"billing.items.type.recurring": "Recurring",
|
|
235
240
|
"billing.items.type.usage": "Usage",
|
|
241
|
+
"billing.mode.postpaid": "Postpaid",
|
|
242
|
+
"billing.mode.prepaid": "Prepaid",
|
|
243
|
+
"billing.prepaid.credit.near_limit": "Near limit",
|
|
244
|
+
"billing.prepaid.adjust.action": "Adjust",
|
|
245
|
+
"billing.prepaid.adjust.amount": "Amount (+credit / −debit)",
|
|
246
|
+
"billing.prepaid.adjust.confirm": "Apply adjustment",
|
|
247
|
+
"billing.prepaid.adjust.description": "Reason",
|
|
248
|
+
"billing.prepaid.adjust.error": "Adjustment failed",
|
|
249
|
+
"billing.prepaid.adjust.invalid_amount": "Enter a non-zero amount",
|
|
250
|
+
"billing.prepaid.adjust.invalid_description": "Enter a reason",
|
|
251
|
+
"billing.prepaid.adjust.submitting": "Applying…",
|
|
252
|
+
"billing.prepaid.adjust.success": "Balance adjusted",
|
|
253
|
+
"billing.prepaid.credit.over_limit": "Over limit",
|
|
254
|
+
"billing.prepaid.credit.within_limit": "Within limit",
|
|
255
|
+
"billing.prepaid.panel.balance": "Balance",
|
|
256
|
+
"billing.prepaid.panel.credit": "Credit",
|
|
257
|
+
"billing.prepaid.panel.last_movement": "Last movement",
|
|
258
|
+
"billing.prepaid.panel.no_transactions": "No balance movements yet.",
|
|
259
|
+
"billing.prepaid.panel.threshold": "Low threshold",
|
|
260
|
+
"billing.prepaid.panel.title": "Prepaid balance",
|
|
261
|
+
"billing.prepaid.panel.transactions": "Balance transactions",
|
|
262
|
+
"billing.prepaid.status.exhausted": "Exhausted",
|
|
263
|
+
"billing.prepaid.status.low": "Low",
|
|
264
|
+
"billing.prepaid.status.ok": "OK",
|
|
265
|
+
"billing.prepaid.topup.action": "Top up",
|
|
266
|
+
"billing.prepaid.topup.amount": "Amount",
|
|
267
|
+
"billing.prepaid.topup.confirm": "Start checkout",
|
|
268
|
+
"billing.prepaid.topup.created": "Top-up session created",
|
|
269
|
+
"billing.prepaid.topup.error": "Top-up failed",
|
|
270
|
+
"billing.prepaid.topup.invalid": "Enter a positive amount",
|
|
271
|
+
"billing.prepaid.topup.submitting": "Starting…",
|
|
236
272
|
"billing.runs.actions.open": "Open",
|
|
237
273
|
"billing.runs.columns.accounts": "Accounts",
|
|
238
274
|
"billing.runs.columns.as_of_date": "As-of date",
|
|
@@ -256,6 +292,10 @@
|
|
|
256
292
|
"billing.runs.detail.title": "Bill Run",
|
|
257
293
|
"billing.runs.filters.status": "Status",
|
|
258
294
|
"billing.runs.filters.triggered_by": "Trigger",
|
|
295
|
+
"billing.runs.mode.catch_up": "Catch-up",
|
|
296
|
+
"billing.runs.mode.dry_run": "Dry-run",
|
|
297
|
+
"billing.runs.mode.real": "Real",
|
|
298
|
+
"billing.runs.mode.test": "Test",
|
|
259
299
|
"billing.runs.outcomes.columns.account": "Account",
|
|
260
300
|
"billing.runs.outcomes.columns.error": "Error",
|
|
261
301
|
"billing.runs.outcomes.columns.invoice": "Invoice",
|
|
@@ -277,10 +317,6 @@
|
|
|
277
317
|
"billing.runs.summary.title": "Summary",
|
|
278
318
|
"billing.runs.summary.usage": "Usage rated",
|
|
279
319
|
"billing.runs.summary.warnings": "Warnings",
|
|
280
|
-
"billing.runs.mode.catch_up": "Catch-up",
|
|
281
|
-
"billing.runs.mode.dry_run": "Dry-run",
|
|
282
|
-
"billing.runs.mode.real": "Real",
|
|
283
|
-
"billing.runs.mode.test": "Test",
|
|
284
320
|
"billing.runs.title": "Bill Runs",
|
|
285
321
|
"billing.runs.trigger.action": "New run",
|
|
286
322
|
"billing.runs.trigger.dialog.as_of_date": "As-of date",
|
|
@@ -299,6 +335,78 @@
|
|
|
299
335
|
"billing.runs.trigger.manual": "Manual",
|
|
300
336
|
"billing.runs.trigger.schedule": "Schedule",
|
|
301
337
|
"billing.runs.trigger.success": "Bill Run triggered",
|
|
338
|
+
"billing.statements.breakdown.amount": "Amount",
|
|
339
|
+
"billing.statements.breakdown.item": "Item",
|
|
340
|
+
"billing.statements.breakdown.quantity": "Qty",
|
|
341
|
+
"billing.statements.breakdown.type": "Type",
|
|
342
|
+
"billing.statements.columns.adjustments": "Adjustments",
|
|
343
|
+
"billing.statements.columns.closing": "Closing",
|
|
344
|
+
"billing.statements.columns.opening": "Opening",
|
|
345
|
+
"billing.statements.columns.period": "Period",
|
|
346
|
+
"billing.statements.columns.recurring": "Recurring",
|
|
347
|
+
"billing.statements.columns.status": "Status",
|
|
348
|
+
"billing.statements.columns.account": "Account",
|
|
349
|
+
"billing.statements.columns.topups": "Top-ups",
|
|
350
|
+
"billing.statements.columns.usage": "Usage",
|
|
351
|
+
"billing.statements.detail.bill_run": "Bill Run",
|
|
352
|
+
"billing.statements.detail.breakdown": "Period-close charges",
|
|
353
|
+
"billing.statements.detail.load_error": "Failed to load statement",
|
|
354
|
+
"billing.statements.detail.loading": "Loading statement…",
|
|
355
|
+
"billing.statements.detail.no_charges": "No recurring charges this period.",
|
|
356
|
+
"billing.statements.detail.not_found": "Statement not found",
|
|
357
|
+
"billing.statements.detail.title": "Statement",
|
|
358
|
+
"billing.statements.detail.view_account": "View account",
|
|
359
|
+
"billing.statements.filters.status": "Status",
|
|
360
|
+
"billing.statements.status.generated": "Generated",
|
|
361
|
+
"billing.statements.status.sent": "Sent",
|
|
362
|
+
"billing.statements.title": "Statements",
|
|
363
|
+
"billing.topups.columns.account": "Account",
|
|
364
|
+
"billing.topups.columns.amount": "Amount",
|
|
365
|
+
"billing.topups.columns.created": "Created",
|
|
366
|
+
"billing.topups.columns.provider": "Provider",
|
|
367
|
+
"billing.topups.columns.status": "Status",
|
|
368
|
+
"billing.topups.detail.balance_txn": "Balance transaction",
|
|
369
|
+
"billing.topups.detail.gateway_txn": "Gateway transaction",
|
|
370
|
+
"billing.topups.detail.load_error": "Failed to load top-up",
|
|
371
|
+
"billing.topups.detail.loading": "Loading top-up…",
|
|
372
|
+
"billing.topups.detail.not_found": "Top-up not found",
|
|
373
|
+
"billing.topups.detail.payment_id": "Payment ID",
|
|
374
|
+
"billing.topups.detail.receipt": "VAT receipt",
|
|
375
|
+
"billing.topups.detail.title": "Top-up",
|
|
376
|
+
"billing.topups.detail.updated": "Updated",
|
|
377
|
+
"billing.topups.detail.view_account": "View account",
|
|
378
|
+
"billing.topups.filters.status": "Status",
|
|
379
|
+
"billing.topups.status.cancelled": "Cancelled",
|
|
380
|
+
"billing.topups.status.captured": "Captured",
|
|
381
|
+
"billing.topups.status.expired": "Expired",
|
|
382
|
+
"billing.topups.status.failed": "Failed",
|
|
383
|
+
"billing.topups.status.pending": "Pending",
|
|
384
|
+
"billing.topups.title": "Top-ups",
|
|
385
|
+
"billing.transactions.columns.amount": "Amount",
|
|
386
|
+
"billing.transactions.columns.balance_after": "Balance",
|
|
387
|
+
"billing.transactions.columns.description": "Description",
|
|
388
|
+
"billing.transactions.columns.type": "Type",
|
|
389
|
+
"billing.transactions.columns.when": "When",
|
|
390
|
+
"billing.transactions.type.adjustment": "Adjustment",
|
|
391
|
+
"billing.transactions.type.one_time": "One-time",
|
|
392
|
+
"billing.transactions.type.recurring": "Recurring",
|
|
393
|
+
"billing.transactions.type.reversal": "Reversal",
|
|
394
|
+
"billing.transactions.type.topup": "Top-up",
|
|
395
|
+
"billing.transactions.type.usage": "Usage",
|
|
396
|
+
"billing.transactions.columns.account": "Account",
|
|
397
|
+
"billing.transactions.detail.item": "Billing item",
|
|
398
|
+
"billing.transactions.detail.load_error": "Failed to load transaction",
|
|
399
|
+
"billing.transactions.detail.loading": "Loading transaction…",
|
|
400
|
+
"billing.transactions.detail.not_found": "Transaction not found",
|
|
401
|
+
"billing.transactions.detail.source_ref": "Source ref",
|
|
402
|
+
"billing.transactions.detail.statement": "Statement",
|
|
403
|
+
"billing.transactions.detail.title": "Transaction",
|
|
404
|
+
"billing.transactions.detail.topup": "Top-up",
|
|
405
|
+
"billing.transactions.detail.usage": "Usage record",
|
|
406
|
+
"billing.transactions.detail.user": "User",
|
|
407
|
+
"billing.transactions.detail.view_account": "View account",
|
|
408
|
+
"billing.transactions.filters.type": "Type",
|
|
409
|
+
"billing.transactions.title": "Transactions",
|
|
302
410
|
"billing.usage.columns.description": "Description",
|
|
303
411
|
"billing.usage.columns.period": "Period",
|
|
304
412
|
"billing.usage.columns.quantity": "Quantity",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"billing.accounts.actions.open": "Otwórz",
|
|
3
|
+
"billing.accounts.columns.credit_limit": "Limit kredytowy",
|
|
3
4
|
"billing.accounts.columns.currency": "Waluta",
|
|
4
5
|
"billing.accounts.columns.customer": "Klient",
|
|
5
6
|
"billing.accounts.columns.cycle": "Cykl",
|
|
6
7
|
"billing.accounts.columns.last_bill": "Ostatnie rozliczenie",
|
|
8
|
+
"billing.accounts.columns.mode": "Tryb",
|
|
7
9
|
"billing.accounts.columns.name": "Nazwa",
|
|
8
10
|
"billing.accounts.columns.next_bill": "Następne rozliczenie",
|
|
9
11
|
"billing.accounts.columns.status": "Status",
|
|
@@ -62,6 +64,8 @@
|
|
|
62
64
|
"billing.accounts.form.error.next_bill_date_required": "Data następnego rozliczenia jest wymagana",
|
|
63
65
|
"billing.accounts.form.invoice_email": "E-mail do faktur",
|
|
64
66
|
"billing.accounts.form.invoice_language": "Język faktury (ISO 639-1)",
|
|
67
|
+
"billing.accounts.form.mode.locked": "Niezmienne — ustalane przy tworzeniu konta.",
|
|
68
|
+
"billing.accounts.form.mode.title": "Tryb rozliczeń i kredyt",
|
|
65
69
|
"billing.accounts.form.name": "Nazwa",
|
|
66
70
|
"billing.accounts.form.name.placeholder": "Uzupełnione z klienta — nadpisz np. „Acme — produkcja”",
|
|
67
71
|
"billing.accounts.form.next_bill_date": "Data następnego rozliczenia",
|
|
@@ -69,6 +73,7 @@
|
|
|
69
73
|
"billing.accounts.form.saving": "Zapisywanie…",
|
|
70
74
|
"billing.accounts.form.submit_hint": "aby zapisać,",
|
|
71
75
|
"billing.accounts.form.tax_id": "NIP / VATIN (opcjonalnie)",
|
|
76
|
+
"billing.accounts.form.threshold.hint": "Puste = domyślne dla najemcy",
|
|
72
77
|
"billing.accounts.title": "Konta rozliczeniowe",
|
|
73
78
|
"billing.common.active": "Aktywne",
|
|
74
79
|
"billing.common.cancel": "Anuluj",
|
|
@@ -233,6 +238,37 @@
|
|
|
233
238
|
"billing.items.type.one_time": "Jednorazowa",
|
|
234
239
|
"billing.items.type.recurring": "Cykliczna",
|
|
235
240
|
"billing.items.type.usage": "Wg zużycia",
|
|
241
|
+
"billing.mode.postpaid": "Postpłata",
|
|
242
|
+
"billing.mode.prepaid": "Przedpłata",
|
|
243
|
+
"billing.prepaid.credit.near_limit": "Blisko limitu",
|
|
244
|
+
"billing.prepaid.adjust.action": "Korekta",
|
|
245
|
+
"billing.prepaid.adjust.amount": "Kwota (+uznanie / −obciążenie)",
|
|
246
|
+
"billing.prepaid.adjust.confirm": "Zastosuj korektę",
|
|
247
|
+
"billing.prepaid.adjust.description": "Powód",
|
|
248
|
+
"billing.prepaid.adjust.error": "Korekta nie powiodła się",
|
|
249
|
+
"billing.prepaid.adjust.invalid_amount": "Podaj kwotę różną od zera",
|
|
250
|
+
"billing.prepaid.adjust.invalid_description": "Podaj powód",
|
|
251
|
+
"billing.prepaid.adjust.submitting": "Stosowanie…",
|
|
252
|
+
"billing.prepaid.adjust.success": "Saldo skorygowane",
|
|
253
|
+
"billing.prepaid.credit.over_limit": "Powyżej limitu",
|
|
254
|
+
"billing.prepaid.credit.within_limit": "W limicie",
|
|
255
|
+
"billing.prepaid.panel.balance": "Saldo",
|
|
256
|
+
"billing.prepaid.panel.credit": "Kredyt",
|
|
257
|
+
"billing.prepaid.panel.last_movement": "Ostatnia zmiana",
|
|
258
|
+
"billing.prepaid.panel.no_transactions": "Brak zmian salda.",
|
|
259
|
+
"billing.prepaid.panel.threshold": "Próg niskiego salda",
|
|
260
|
+
"billing.prepaid.panel.title": "Saldo przedpłacone",
|
|
261
|
+
"billing.prepaid.panel.transactions": "Transakcje salda",
|
|
262
|
+
"billing.prepaid.status.exhausted": "Wyczerpane",
|
|
263
|
+
"billing.prepaid.status.low": "Niskie",
|
|
264
|
+
"billing.prepaid.status.ok": "OK",
|
|
265
|
+
"billing.prepaid.topup.action": "Doładuj",
|
|
266
|
+
"billing.prepaid.topup.amount": "Kwota",
|
|
267
|
+
"billing.prepaid.topup.confirm": "Rozpocznij płatność",
|
|
268
|
+
"billing.prepaid.topup.created": "Utworzono sesję doładowania",
|
|
269
|
+
"billing.prepaid.topup.error": "Doładowanie nie powiodło się",
|
|
270
|
+
"billing.prepaid.topup.invalid": "Podaj dodatnią kwotę",
|
|
271
|
+
"billing.prepaid.topup.submitting": "Rozpoczynanie…",
|
|
236
272
|
"billing.runs.actions.open": "Otwórz",
|
|
237
273
|
"billing.runs.columns.accounts": "Konta",
|
|
238
274
|
"billing.runs.columns.as_of_date": "Data rozliczenia",
|
|
@@ -256,6 +292,10 @@
|
|
|
256
292
|
"billing.runs.detail.title": "Przebieg rozliczeniowy",
|
|
257
293
|
"billing.runs.filters.status": "Status",
|
|
258
294
|
"billing.runs.filters.triggered_by": "Wyzwalacz",
|
|
295
|
+
"billing.runs.mode.catch_up": "Nadrabianie",
|
|
296
|
+
"billing.runs.mode.dry_run": "Dry-run",
|
|
297
|
+
"billing.runs.mode.real": "Realny",
|
|
298
|
+
"billing.runs.mode.test": "Testowy",
|
|
259
299
|
"billing.runs.outcomes.columns.account": "Konto",
|
|
260
300
|
"billing.runs.outcomes.columns.error": "Błąd",
|
|
261
301
|
"billing.runs.outcomes.columns.invoice": "Faktura",
|
|
@@ -277,10 +317,6 @@
|
|
|
277
317
|
"billing.runs.summary.title": "Podsumowanie",
|
|
278
318
|
"billing.runs.summary.usage": "Rozliczone zużycie",
|
|
279
319
|
"billing.runs.summary.warnings": "Ostrzeżenia",
|
|
280
|
-
"billing.runs.mode.catch_up": "Nadrabianie",
|
|
281
|
-
"billing.runs.mode.dry_run": "Dry-run",
|
|
282
|
-
"billing.runs.mode.real": "Realny",
|
|
283
|
-
"billing.runs.mode.test": "Testowy",
|
|
284
320
|
"billing.runs.title": "Przebiegi rozliczeniowe",
|
|
285
321
|
"billing.runs.trigger.action": "Nowy przebieg",
|
|
286
322
|
"billing.runs.trigger.dialog.as_of_date": "Data odniesienia",
|
|
@@ -299,6 +335,78 @@
|
|
|
299
335
|
"billing.runs.trigger.manual": "Ręcznie",
|
|
300
336
|
"billing.runs.trigger.schedule": "Harmonogram",
|
|
301
337
|
"billing.runs.trigger.success": "Przebieg rozliczeniowy uruchomiony",
|
|
338
|
+
"billing.statements.breakdown.amount": "Kwota",
|
|
339
|
+
"billing.statements.breakdown.item": "Pozycja",
|
|
340
|
+
"billing.statements.breakdown.quantity": "Ilość",
|
|
341
|
+
"billing.statements.breakdown.type": "Typ",
|
|
342
|
+
"billing.statements.columns.adjustments": "Korekty",
|
|
343
|
+
"billing.statements.columns.closing": "Zamknięcie",
|
|
344
|
+
"billing.statements.columns.opening": "Otwarcie",
|
|
345
|
+
"billing.statements.columns.period": "Okres",
|
|
346
|
+
"billing.statements.columns.recurring": "Cykliczne",
|
|
347
|
+
"billing.statements.columns.status": "Status",
|
|
348
|
+
"billing.statements.columns.account": "Konto",
|
|
349
|
+
"billing.statements.columns.topups": "Doładowania",
|
|
350
|
+
"billing.statements.columns.usage": "Zużycie",
|
|
351
|
+
"billing.statements.detail.bill_run": "Przebieg rozliczeń",
|
|
352
|
+
"billing.statements.detail.breakdown": "Opłaty na koniec okresu",
|
|
353
|
+
"billing.statements.detail.load_error": "Nie udało się wczytać zestawienia",
|
|
354
|
+
"billing.statements.detail.loading": "Wczytywanie zestawienia…",
|
|
355
|
+
"billing.statements.detail.no_charges": "Brak opłat cyklicznych w tym okresie.",
|
|
356
|
+
"billing.statements.detail.not_found": "Nie znaleziono zestawienia",
|
|
357
|
+
"billing.statements.detail.title": "Zestawienie",
|
|
358
|
+
"billing.statements.detail.view_account": "Zobacz konto",
|
|
359
|
+
"billing.statements.filters.status": "Status",
|
|
360
|
+
"billing.statements.status.generated": "Wygenerowane",
|
|
361
|
+
"billing.statements.status.sent": "Wysłane",
|
|
362
|
+
"billing.statements.title": "Zestawienia",
|
|
363
|
+
"billing.topups.columns.account": "Konto",
|
|
364
|
+
"billing.topups.columns.amount": "Kwota",
|
|
365
|
+
"billing.topups.columns.created": "Utworzono",
|
|
366
|
+
"billing.topups.columns.provider": "Dostawca",
|
|
367
|
+
"billing.topups.columns.status": "Status",
|
|
368
|
+
"billing.topups.detail.balance_txn": "Transakcja salda",
|
|
369
|
+
"billing.topups.detail.gateway_txn": "Transakcja bramki",
|
|
370
|
+
"billing.topups.detail.load_error": "Nie udało się wczytać doładowania",
|
|
371
|
+
"billing.topups.detail.loading": "Wczytywanie doładowania…",
|
|
372
|
+
"billing.topups.detail.not_found": "Nie znaleziono doładowania",
|
|
373
|
+
"billing.topups.detail.payment_id": "Identyfikator płatności",
|
|
374
|
+
"billing.topups.detail.receipt": "Faktura VAT",
|
|
375
|
+
"billing.topups.detail.title": "Doładowanie",
|
|
376
|
+
"billing.topups.detail.updated": "Zaktualizowano",
|
|
377
|
+
"billing.topups.detail.view_account": "Zobacz konto",
|
|
378
|
+
"billing.topups.filters.status": "Status",
|
|
379
|
+
"billing.topups.status.cancelled": "Anulowane",
|
|
380
|
+
"billing.topups.status.captured": "Zaksięgowane",
|
|
381
|
+
"billing.topups.status.expired": "Wygasłe",
|
|
382
|
+
"billing.topups.status.failed": "Nieudane",
|
|
383
|
+
"billing.topups.status.pending": "Oczekujące",
|
|
384
|
+
"billing.topups.title": "Doładowania",
|
|
385
|
+
"billing.transactions.columns.amount": "Kwota",
|
|
386
|
+
"billing.transactions.columns.balance_after": "Saldo",
|
|
387
|
+
"billing.transactions.columns.description": "Opis",
|
|
388
|
+
"billing.transactions.columns.type": "Typ",
|
|
389
|
+
"billing.transactions.columns.when": "Kiedy",
|
|
390
|
+
"billing.transactions.type.adjustment": "Korekta",
|
|
391
|
+
"billing.transactions.type.one_time": "Jednorazowe",
|
|
392
|
+
"billing.transactions.type.recurring": "Cykliczne",
|
|
393
|
+
"billing.transactions.type.reversal": "Storno",
|
|
394
|
+
"billing.transactions.type.topup": "Doładowanie",
|
|
395
|
+
"billing.transactions.type.usage": "Zużycie",
|
|
396
|
+
"billing.transactions.columns.account": "Konto",
|
|
397
|
+
"billing.transactions.detail.item": "Pozycja rozliczeniowa",
|
|
398
|
+
"billing.transactions.detail.load_error": "Nie udało się wczytać transakcji",
|
|
399
|
+
"billing.transactions.detail.loading": "Wczytywanie transakcji…",
|
|
400
|
+
"billing.transactions.detail.not_found": "Nie znaleziono transakcji",
|
|
401
|
+
"billing.transactions.detail.source_ref": "Referencja źródłowa",
|
|
402
|
+
"billing.transactions.detail.statement": "Zestawienie",
|
|
403
|
+
"billing.transactions.detail.title": "Transakcja",
|
|
404
|
+
"billing.transactions.detail.topup": "Doładowanie",
|
|
405
|
+
"billing.transactions.detail.usage": "Rekord zużycia",
|
|
406
|
+
"billing.transactions.detail.user": "Użytkownik",
|
|
407
|
+
"billing.transactions.detail.view_account": "Zobacz konto",
|
|
408
|
+
"billing.transactions.filters.type": "Typ",
|
|
409
|
+
"billing.transactions.title": "Transakcje",
|
|
302
410
|
"billing.usage.columns.description": "Opis",
|
|
303
411
|
"billing.usage.columns.period": "Okres",
|
|
304
412
|
"billing.usage.columns.quantity": "Ilość",
|
|
@@ -15,7 +15,7 @@ export const metadata: ModuleInfo = {
|
|
|
15
15
|
'Lightweight recurring-billing engine — collects one-time / recurring / usage items, runs Bill Runs, produces draft invoices in core/sales.',
|
|
16
16
|
author: 'Dainamite',
|
|
17
17
|
license: 'MIT',
|
|
18
|
-
requires: ['auth', 'directory', 'dictionaries', 'configs', 'sales'],
|
|
18
|
+
requires: ['auth', 'directory', 'dictionaries', 'configs', 'sales', 'payment_gateways'],
|
|
19
19
|
ejectable: false,
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { BillingAccount, BillingAccountBalance } from '../data/entities'
|
|
3
|
+
import type { BalanceStatus, BillingMode } from '../data/validators'
|
|
4
|
+
import {
|
|
5
|
+
computeBalanceStatus,
|
|
6
|
+
resolveLowBalanceThreshold,
|
|
7
|
+
} from './balanceStatus'
|
|
8
|
+
import {
|
|
9
|
+
computeCreditSnapshot,
|
|
10
|
+
detectCreditCrossing,
|
|
11
|
+
type CreditSnapshot,
|
|
12
|
+
} from './creditStatus'
|
|
13
|
+
import type { DomainEvent } from './balanceEvents'
|
|
14
|
+
import { formatMoney, subMoney, toUnits } from './money'
|
|
15
|
+
import {
|
|
16
|
+
getLowBalanceThresholdDefault,
|
|
17
|
+
getNearLimitBufferDefault,
|
|
18
|
+
} from './prepaidConfig'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Read-model helpers for an account's prepaid balance + credit status
|
|
22
|
+
* (SPEC-002). Shared by `GET …/balance`, `GET …/credit-status`, and the
|
|
23
|
+
* admin Prepaid panel so the math lives in exactly one place.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export type BalanceSnapshot = {
|
|
27
|
+
billAccountId: string
|
|
28
|
+
currencyCode: string
|
|
29
|
+
balance: string
|
|
30
|
+
balanceStatus: BalanceStatus
|
|
31
|
+
/** The resolved threshold (account row → tenant config default). */
|
|
32
|
+
lowBalanceThreshold: string
|
|
33
|
+
lastMovementAt: string | null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function getBalanceSnapshot(
|
|
37
|
+
em: EntityManager,
|
|
38
|
+
account: BillingAccount,
|
|
39
|
+
): Promise<BalanceSnapshot> {
|
|
40
|
+
const row = await em.findOne(BillingAccountBalance, {
|
|
41
|
+
tenantId: account.tenantId,
|
|
42
|
+
billAccountId: account.id,
|
|
43
|
+
})
|
|
44
|
+
const balance = row?.balance ?? '0.0000'
|
|
45
|
+
const threshold = resolveLowBalanceThreshold(
|
|
46
|
+
row?.lowBalanceThreshold ?? null,
|
|
47
|
+
await getLowBalanceThresholdDefault(em),
|
|
48
|
+
)
|
|
49
|
+
return {
|
|
50
|
+
billAccountId: account.id,
|
|
51
|
+
currencyCode: account.currencyCode,
|
|
52
|
+
balance: formatMoney(balance),
|
|
53
|
+
balanceStatus: computeBalanceStatus(balance, threshold),
|
|
54
|
+
lowBalanceThreshold: formatMoney(threshold),
|
|
55
|
+
lastMovementAt: row?.lastMovementAt ? row.lastMovementAt.toISOString() : null,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Σ outstanding of POSTED (not-yet-paid) core/sales invoices the engine
|
|
61
|
+
* emitted for this account — the postpaid `credit_used` (v1; mid-period
|
|
62
|
+
* accrued usage is not counted, see SPEC-002 Open Question #5). Top-up
|
|
63
|
+
* receipts are `paid`, so they never count here.
|
|
64
|
+
*/
|
|
65
|
+
export async function getPostpaidOutstanding(
|
|
66
|
+
em: EntityManager,
|
|
67
|
+
account: BillingAccount,
|
|
68
|
+
): Promise<string> {
|
|
69
|
+
const rows = (await em.execute(
|
|
70
|
+
`SELECT COALESCE(SUM(outstanding_amount), 0) AS total
|
|
71
|
+
FROM sales_invoices
|
|
72
|
+
WHERE tenant_id = ?
|
|
73
|
+
AND organization_id = ?
|
|
74
|
+
AND deleted_at IS NULL
|
|
75
|
+
AND status = 'posted'
|
|
76
|
+
AND metadata->>'bill_account_id' = ?`,
|
|
77
|
+
[account.tenantId, account.organizationId, account.id],
|
|
78
|
+
)) as unknown as Array<{ total: string }>
|
|
79
|
+
return formatMoney(rows?.[0]?.total ?? 0)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type CreditSnapshotResult = CreditSnapshot & { currencyCode: string }
|
|
83
|
+
|
|
84
|
+
export async function getCreditSnapshot(
|
|
85
|
+
em: EntityManager,
|
|
86
|
+
account: BillingAccount,
|
|
87
|
+
): Promise<CreditSnapshotResult> {
|
|
88
|
+
const mode = (account.billingMode as BillingMode) ?? 'postpaid'
|
|
89
|
+
const nearLimitBuffer = await getNearLimitBufferDefault(em)
|
|
90
|
+
|
|
91
|
+
let balance: string | null = null
|
|
92
|
+
let outstanding: string | null = null
|
|
93
|
+
if (mode === 'prepaid') {
|
|
94
|
+
const row = await em.findOne(BillingAccountBalance, {
|
|
95
|
+
tenantId: account.tenantId,
|
|
96
|
+
billAccountId: account.id,
|
|
97
|
+
})
|
|
98
|
+
balance = row?.balance ?? '0.0000'
|
|
99
|
+
} else {
|
|
100
|
+
outstanding = await getPostpaidOutstanding(em, account)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const snapshot = computeCreditSnapshot({
|
|
104
|
+
mode,
|
|
105
|
+
creditLimit: account.creditLimit ?? '0',
|
|
106
|
+
nearLimitBuffer,
|
|
107
|
+
balance,
|
|
108
|
+
outstanding,
|
|
109
|
+
})
|
|
110
|
+
return { ...snapshot, currencyCode: account.currencyCode }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Credit-crossing events for a postpaid account when a draft invoice is posted
|
|
115
|
+
* (SPEC-002 P6). `billing.credit.over_limit` fires once on the upward crossing
|
|
116
|
+
* of `credit_used` (Σ posted-unpaid outstanding) past `credit_limit`. The
|
|
117
|
+
* just-posted invoice's `outstanding` is already included in the current total,
|
|
118
|
+
* so the prior total is `current − justPostedOutstanding`.
|
|
119
|
+
*/
|
|
120
|
+
export async function buildPostpaidCreditCrossingEvents(
|
|
121
|
+
em: EntityManager,
|
|
122
|
+
account: BillingAccount,
|
|
123
|
+
justPostedOutstanding: string,
|
|
124
|
+
): Promise<DomainEvent[]> {
|
|
125
|
+
if ((account.billingMode as BillingMode) !== 'postpaid') return []
|
|
126
|
+
const creditLimit = account.creditLimit ?? '0'
|
|
127
|
+
if (toUnits(creditLimit) <= 0) return []
|
|
128
|
+
|
|
129
|
+
const nearLimitBuffer = await getNearLimitBufferDefault(em)
|
|
130
|
+
const usedAfter = await getPostpaidOutstanding(em, account)
|
|
131
|
+
const usedBefore = subMoney(usedAfter, justPostedOutstanding)
|
|
132
|
+
const crossing = detectCreditCrossing({
|
|
133
|
+
creditLimit,
|
|
134
|
+
nearLimitBuffer,
|
|
135
|
+
usedBefore,
|
|
136
|
+
usedAfter,
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const events: DomainEvent[] = []
|
|
140
|
+
const base = {
|
|
141
|
+
billAccountId: account.id,
|
|
142
|
+
tenantId: account.tenantId,
|
|
143
|
+
organizationId: account.organizationId,
|
|
144
|
+
currencyCode: account.currencyCode,
|
|
145
|
+
creditLimit,
|
|
146
|
+
creditUsed: usedAfter,
|
|
147
|
+
}
|
|
148
|
+
if (crossing.overLimitCrossedUp) {
|
|
149
|
+
events.push({ id: 'billing.credit.over_limit', payload: { ...base } })
|
|
150
|
+
}
|
|
151
|
+
if (crossing.nearLimitCrossedUp) {
|
|
152
|
+
events.push({ id: 'billing.credit.near_limit', payload: { ...base } })
|
|
153
|
+
}
|
|
154
|
+
return events
|
|
155
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { detectBalanceCrossing } from './balanceStatus'
|
|
2
|
+
import { detectCreditCrossing, prepaidCreditUsed } from './creditStatus'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared balance/credit threshold-crossing → domain-event builder (SPEC-002).
|
|
6
|
+
*
|
|
7
|
+
* Every balance movement that can change status — real-time usage debit, a
|
|
8
|
+
* manual adjustment, a period-close recurring charge — runs through this so
|
|
9
|
+
* the "fire only on the relevant downward/upward crossing" rule lives in
|
|
10
|
+
* exactly one place. The events are returned (not emitted) so callers fire
|
|
11
|
+
* them AFTER the DB commit.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type DomainEvent = { id: string; payload: Record<string, unknown> }
|
|
15
|
+
|
|
16
|
+
export type BalanceEventScope = {
|
|
17
|
+
billAccountId: string
|
|
18
|
+
tenantId: string
|
|
19
|
+
organizationId: string
|
|
20
|
+
currencyCode: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function buildBalanceCrossingEvents(params: {
|
|
24
|
+
scope: BalanceEventScope
|
|
25
|
+
balanceBefore: string
|
|
26
|
+
balanceAfter: string
|
|
27
|
+
threshold: string
|
|
28
|
+
creditLimit: string
|
|
29
|
+
nearLimitBuffer: string
|
|
30
|
+
}): DomainEvent[] {
|
|
31
|
+
const { scope, balanceBefore, balanceAfter, threshold, creditLimit, nearLimitBuffer } = params
|
|
32
|
+
const events: DomainEvent[] = []
|
|
33
|
+
const base = {
|
|
34
|
+
billAccountId: scope.billAccountId,
|
|
35
|
+
tenantId: scope.tenantId,
|
|
36
|
+
organizationId: scope.organizationId,
|
|
37
|
+
currencyCode: scope.currencyCode,
|
|
38
|
+
balance: balanceAfter,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const balanceCrossing = detectBalanceCrossing(balanceBefore, balanceAfter, threshold)
|
|
42
|
+
if (balanceCrossing.exhaustedCrossedDown) {
|
|
43
|
+
events.push({ id: 'billing.balance.exhausted', payload: { ...base } })
|
|
44
|
+
}
|
|
45
|
+
if (balanceCrossing.lowCrossedDown) {
|
|
46
|
+
events.push({
|
|
47
|
+
id: 'billing.balance.low',
|
|
48
|
+
payload: { ...base, lowBalanceThreshold: threshold },
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const usedBefore = prepaidCreditUsed(balanceBefore)
|
|
53
|
+
const usedAfter = prepaidCreditUsed(balanceAfter)
|
|
54
|
+
const creditCrossing = detectCreditCrossing({
|
|
55
|
+
creditLimit,
|
|
56
|
+
nearLimitBuffer,
|
|
57
|
+
usedBefore,
|
|
58
|
+
usedAfter,
|
|
59
|
+
})
|
|
60
|
+
if (creditCrossing.overLimitCrossedUp) {
|
|
61
|
+
events.push({
|
|
62
|
+
id: 'billing.credit.over_limit',
|
|
63
|
+
payload: { ...base, creditLimit, creditUsed: usedAfter },
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
if (creditCrossing.nearLimitCrossedUp) {
|
|
67
|
+
events.push({
|
|
68
|
+
id: 'billing.credit.near_limit',
|
|
69
|
+
payload: { ...base, creditLimit, creditUsed: usedAfter },
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return events
|
|
74
|
+
}
|