@dainamite/billing 0.17.0 → 0.19.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.
Files changed (206) hide show
  1. package/CHANGELOG.md +99 -19
  2. package/dist/modules/billing/acl.js +9 -1
  3. package/dist/modules/billing/acl.js.map +2 -2
  4. package/dist/modules/billing/api/accounts/[id]/balance/route.js +62 -0
  5. package/dist/modules/billing/api/accounts/[id]/balance/route.js.map +7 -0
  6. package/dist/modules/billing/api/accounts/[id]/credit-status/route.js +56 -0
  7. package/dist/modules/billing/api/accounts/[id]/credit-status/route.js.map +7 -0
  8. package/dist/modules/billing/api/accounts/[id]/topups/route.js +36 -0
  9. package/dist/modules/billing/api/accounts/[id]/topups/route.js.map +7 -0
  10. package/dist/modules/billing/api/accounts/route.js +7 -0
  11. package/dist/modules/billing/api/accounts/route.js.map +2 -2
  12. package/dist/modules/billing/api/export/account/[id]/route.js +48 -3
  13. package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
  14. package/dist/modules/billing/api/statements/generate/route.js +68 -0
  15. package/dist/modules/billing/api/statements/generate/route.js.map +7 -0
  16. package/dist/modules/billing/api/statements/route.js +110 -0
  17. package/dist/modules/billing/api/statements/route.js.map +7 -0
  18. package/dist/modules/billing/api/topups/route.js +139 -0
  19. package/dist/modules/billing/api/topups/route.js.map +7 -0
  20. package/dist/modules/billing/api/transactions/adjust/route.js +71 -0
  21. package/dist/modules/billing/api/transactions/adjust/route.js.map +7 -0
  22. package/dist/modules/billing/api/transactions/route.js +109 -0
  23. package/dist/modules/billing/api/transactions/route.js.map +7 -0
  24. package/dist/modules/billing/api/usage/route.js +30 -6
  25. package/dist/modules/billing/api/usage/route.js.map +2 -2
  26. package/dist/modules/billing/backend/billing/accounts/[id]/page.js +16 -1
  27. package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
  28. package/dist/modules/billing/backend/billing/accounts/create/page.js +8 -1
  29. package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
  30. package/dist/modules/billing/backend/billing/accounts/page.js +26 -1
  31. package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
  32. package/dist/modules/billing/backend/billing/statements/[id]/page.js +129 -0
  33. package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +7 -0
  34. package/dist/modules/billing/backend/billing/statements/[id]/page.meta.js +21 -0
  35. package/dist/modules/billing/backend/billing/statements/[id]/page.meta.js.map +7 -0
  36. package/dist/modules/billing/backend/billing/statements/page.js +152 -0
  37. package/dist/modules/billing/backend/billing/statements/page.js.map +7 -0
  38. package/dist/modules/billing/backend/billing/statements/page.meta.js +18 -0
  39. package/dist/modules/billing/backend/billing/statements/page.meta.js.map +7 -0
  40. package/dist/modules/billing/backend/billing/topups/[id]/page.js +90 -0
  41. package/dist/modules/billing/backend/billing/topups/[id]/page.js.map +7 -0
  42. package/dist/modules/billing/backend/billing/topups/[id]/page.meta.js +21 -0
  43. package/dist/modules/billing/backend/billing/topups/[id]/page.meta.js.map +7 -0
  44. package/dist/modules/billing/backend/billing/topups/page.js +147 -0
  45. package/dist/modules/billing/backend/billing/topups/page.js.map +7 -0
  46. package/dist/modules/billing/backend/billing/topups/page.meta.js +18 -0
  47. package/dist/modules/billing/backend/billing/topups/page.meta.js.map +7 -0
  48. package/dist/modules/billing/backend/billing/transactions/[id]/page.js +114 -0
  49. package/dist/modules/billing/backend/billing/transactions/[id]/page.js.map +7 -0
  50. package/dist/modules/billing/backend/billing/transactions/[id]/page.meta.js +21 -0
  51. package/dist/modules/billing/backend/billing/transactions/[id]/page.meta.js.map +7 -0
  52. package/dist/modules/billing/backend/billing/transactions/page.js +149 -0
  53. package/dist/modules/billing/backend/billing/transactions/page.js.map +7 -0
  54. package/dist/modules/billing/backend/billing/transactions/page.meta.js +18 -0
  55. package/dist/modules/billing/backend/billing/transactions/page.meta.js.map +7 -0
  56. package/dist/modules/billing/commands/accounts.js +26 -1
  57. package/dist/modules/billing/commands/accounts.js.map +2 -2
  58. package/dist/modules/billing/commands/index.js +3 -0
  59. package/dist/modules/billing/commands/index.js.map +2 -2
  60. package/dist/modules/billing/commands/invoices.js +21 -1
  61. package/dist/modules/billing/commands/invoices.js.map +2 -2
  62. package/dist/modules/billing/commands/statements.js +74 -0
  63. package/dist/modules/billing/commands/statements.js.map +7 -0
  64. package/dist/modules/billing/commands/topups.js +139 -0
  65. package/dist/modules/billing/commands/topups.js.map +7 -0
  66. package/dist/modules/billing/commands/transactions.js +119 -0
  67. package/dist/modules/billing/commands/transactions.js.map +7 -0
  68. package/dist/modules/billing/commands/usage.js +72 -0
  69. package/dist/modules/billing/commands/usage.js.map +2 -2
  70. package/dist/modules/billing/components/AccountForm.js +53 -1
  71. package/dist/modules/billing/components/AccountForm.js.map +2 -2
  72. package/dist/modules/billing/components/PrepaidPanel.js +314 -0
  73. package/dist/modules/billing/components/PrepaidPanel.js.map +7 -0
  74. package/dist/modules/billing/components/accountNames.js +20 -0
  75. package/dist/modules/billing/components/accountNames.js.map +7 -0
  76. package/dist/modules/billing/data/entities.js +290 -0
  77. package/dist/modules/billing/data/entities.js.map +2 -2
  78. package/dist/modules/billing/data/entityIds.js +6 -1
  79. package/dist/modules/billing/data/entityIds.js.map +2 -2
  80. package/dist/modules/billing/data/validators.js +107 -2
  81. package/dist/modules/billing/data/validators.js.map +2 -2
  82. package/dist/modules/billing/events.js +52 -0
  83. package/dist/modules/billing/events.js.map +2 -2
  84. package/dist/modules/billing/i18n/en.json +112 -4
  85. package/dist/modules/billing/i18n/pl.json +112 -4
  86. package/dist/modules/billing/index.js +1 -1
  87. package/dist/modules/billing/index.js.map +2 -2
  88. package/dist/modules/billing/lib/accountStatus.js +106 -0
  89. package/dist/modules/billing/lib/accountStatus.js.map +7 -0
  90. package/dist/modules/billing/lib/balanceEvents.js +48 -0
  91. package/dist/modules/billing/lib/balanceEvents.js.map +7 -0
  92. package/dist/modules/billing/lib/balanceLedger.js +136 -0
  93. package/dist/modules/billing/lib/balanceLedger.js.map +7 -0
  94. package/dist/modules/billing/lib/balanceStatus.js +29 -0
  95. package/dist/modules/billing/lib/balanceStatus.js.map +7 -0
  96. package/dist/modules/billing/lib/billRunEngine.js +40 -2
  97. package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
  98. package/dist/modules/billing/lib/creditStatus.js +65 -0
  99. package/dist/modules/billing/lib/creditStatus.js.map +7 -0
  100. package/dist/modules/billing/lib/money.js +47 -0
  101. package/dist/modules/billing/lib/money.js.map +7 -0
  102. package/dist/modules/billing/lib/prepaidConfig.js +45 -0
  103. package/dist/modules/billing/lib/prepaidConfig.js.map +7 -0
  104. package/dist/modules/billing/lib/prepaidConsume.js +214 -0
  105. package/dist/modules/billing/lib/prepaidConsume.js.map +7 -0
  106. package/dist/modules/billing/lib/prepaidPeriodClose.js +285 -0
  107. package/dist/modules/billing/lib/prepaidPeriodClose.js.map +7 -0
  108. package/dist/modules/billing/lib/topupCapture.js +62 -0
  109. package/dist/modules/billing/lib/topupCapture.js.map +7 -0
  110. package/dist/modules/billing/lib/topupReceipt.js +97 -0
  111. package/dist/modules/billing/lib/topupReceipt.js.map +7 -0
  112. package/dist/modules/billing/lib/topupTransition.js +19 -0
  113. package/dist/modules/billing/lib/topupTransition.js.map +7 -0
  114. package/dist/modules/billing/migrations/Migration20260619000000_billing_prepaid.js +110 -0
  115. package/dist/modules/billing/migrations/Migration20260619000000_billing_prepaid.js.map +7 -0
  116. package/dist/modules/billing/setup.js +32 -1
  117. package/dist/modules/billing/setup.js.map +2 -2
  118. package/dist/modules/billing/subscribers/prepaid-topup-cancelled.js +26 -0
  119. package/dist/modules/billing/subscribers/prepaid-topup-cancelled.js.map +7 -0
  120. package/dist/modules/billing/subscribers/prepaid-topup-captured.js +87 -0
  121. package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +7 -0
  122. package/dist/modules/billing/subscribers/prepaid-topup-expired.js +26 -0
  123. package/dist/modules/billing/subscribers/prepaid-topup-expired.js.map +7 -0
  124. package/dist/modules/billing/subscribers/prepaid-topup-failed.js +26 -0
  125. package/dist/modules/billing/subscribers/prepaid-topup-failed.js.map +7 -0
  126. package/dist/modules/billing/workers/reconcile-prepaid-balances.js +50 -0
  127. package/dist/modules/billing/workers/reconcile-prepaid-balances.js.map +7 -0
  128. package/package.json +9 -9
  129. package/src/modules/billing/__tests__/commands/invoices.test.ts +3 -3
  130. package/src/modules/billing/__tests__/commands/topups.test.ts +104 -0
  131. package/src/modules/billing/__tests__/commands/transactions.test.ts +107 -0
  132. package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +235 -0
  133. package/src/modules/billing/__tests__/lib/accountStatus.test.ts +93 -0
  134. package/src/modules/billing/__tests__/lib/balanceLedger.test.ts +125 -0
  135. package/src/modules/billing/__tests__/lib/balanceStatus.test.ts +65 -0
  136. package/src/modules/billing/__tests__/lib/creditStatus.test.ts +111 -0
  137. package/src/modules/billing/__tests__/lib/money.test.ts +56 -0
  138. package/src/modules/billing/__tests__/lib/prepaidConsume.test.ts +179 -0
  139. package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +232 -0
  140. package/src/modules/billing/__tests__/lib/topupCapture.test.ts +123 -0
  141. package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +84 -0
  142. package/src/modules/billing/__tests__/setup.test.ts +28 -2
  143. package/src/modules/billing/acl.ts +8 -0
  144. package/src/modules/billing/api/accounts/[id]/balance/route.ts +74 -0
  145. package/src/modules/billing/api/accounts/[id]/credit-status/route.ts +71 -0
  146. package/src/modules/billing/api/accounts/[id]/topups/route.ts +49 -0
  147. package/src/modules/billing/api/accounts/route.ts +7 -0
  148. package/src/modules/billing/api/export/account/[id]/route.ts +47 -2
  149. package/src/modules/billing/api/statements/generate/route.ts +78 -0
  150. package/src/modules/billing/api/statements/route.ts +117 -0
  151. package/src/modules/billing/api/topups/route.ts +154 -0
  152. package/src/modules/billing/api/transactions/adjust/route.ts +80 -0
  153. package/src/modules/billing/api/transactions/route.ts +116 -0
  154. package/src/modules/billing/api/usage/route.ts +36 -5
  155. package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +28 -0
  156. package/src/modules/billing/backend/billing/accounts/create/page.tsx +7 -0
  157. package/src/modules/billing/backend/billing/accounts/page.tsx +32 -1
  158. package/src/modules/billing/backend/billing/statements/[id]/page.meta.ts +19 -0
  159. package/src/modules/billing/backend/billing/statements/[id]/page.tsx +198 -0
  160. package/src/modules/billing/backend/billing/statements/page.meta.ts +16 -0
  161. package/src/modules/billing/backend/billing/statements/page.tsx +190 -0
  162. package/src/modules/billing/backend/billing/topups/[id]/page.meta.ts +19 -0
  163. package/src/modules/billing/backend/billing/topups/[id]/page.tsx +151 -0
  164. package/src/modules/billing/backend/billing/topups/page.meta.ts +16 -0
  165. package/src/modules/billing/backend/billing/topups/page.tsx +182 -0
  166. package/src/modules/billing/backend/billing/transactions/[id]/page.meta.ts +19 -0
  167. package/src/modules/billing/backend/billing/transactions/[id]/page.tsx +179 -0
  168. package/src/modules/billing/backend/billing/transactions/page.meta.ts +16 -0
  169. package/src/modules/billing/backend/billing/transactions/page.tsx +187 -0
  170. package/src/modules/billing/commands/accounts.ts +42 -1
  171. package/src/modules/billing/commands/index.ts +3 -0
  172. package/src/modules/billing/commands/invoices.ts +26 -2
  173. package/src/modules/billing/commands/statements.ts +101 -0
  174. package/src/modules/billing/commands/topups.ts +181 -0
  175. package/src/modules/billing/commands/transactions.ts +152 -0
  176. package/src/modules/billing/commands/usage.ts +114 -4
  177. package/src/modules/billing/components/AccountForm.tsx +66 -0
  178. package/src/modules/billing/components/PrepaidPanel.tsx +382 -0
  179. package/src/modules/billing/components/accountNames.ts +23 -0
  180. package/src/modules/billing/data/entities.ts +383 -0
  181. package/src/modules/billing/data/entityIds.ts +5 -0
  182. package/src/modules/billing/data/validators.ts +144 -0
  183. package/src/modules/billing/events.ts +52 -0
  184. package/src/modules/billing/i18n/en.json +112 -4
  185. package/src/modules/billing/i18n/pl.json +112 -4
  186. package/src/modules/billing/index.ts +1 -1
  187. package/src/modules/billing/lib/accountStatus.ts +155 -0
  188. package/src/modules/billing/lib/balanceEvents.ts +74 -0
  189. package/src/modules/billing/lib/balanceLedger.ts +258 -0
  190. package/src/modules/billing/lib/balanceStatus.ts +78 -0
  191. package/src/modules/billing/lib/billRunEngine.ts +66 -5
  192. package/src/modules/billing/lib/creditStatus.ts +153 -0
  193. package/src/modules/billing/lib/money.ts +73 -0
  194. package/src/modules/billing/lib/prepaidConfig.ts +58 -0
  195. package/src/modules/billing/lib/prepaidConsume.ts +299 -0
  196. package/src/modules/billing/lib/prepaidPeriodClose.ts +453 -0
  197. package/src/modules/billing/lib/topupCapture.ts +117 -0
  198. package/src/modules/billing/lib/topupReceipt.ts +153 -0
  199. package/src/modules/billing/lib/topupTransition.ts +31 -0
  200. package/src/modules/billing/migrations/Migration20260619000000_billing_prepaid.ts +131 -0
  201. package/src/modules/billing/setup.ts +45 -0
  202. package/src/modules/billing/subscribers/prepaid-topup-cancelled.ts +31 -0
  203. package/src/modules/billing/subscribers/prepaid-topup-captured.ts +120 -0
  204. package/src/modules/billing/subscribers/prepaid-topup-expired.ts +31 -0
  205. package/src/modules/billing/subscribers/prepaid-topup-failed.ts +32 -0
  206. 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
+ }