@dainamite/billing 0.20.0 → 0.21.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 (598) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/modules/billing/acl.js +13 -1
  3. package/dist/modules/billing/acl.js.map +2 -2
  4. package/dist/modules/billing/api/accounts/[id]/summary/route.js +52 -0
  5. package/dist/modules/billing/api/accounts/[id]/summary/route.js.map +7 -0
  6. package/dist/modules/billing/api/accounts/route.js +46 -4
  7. package/dist/modules/billing/api/accounts/route.js.map +2 -2
  8. package/dist/modules/billing/api/export/account/[id]/route.js +11 -7
  9. package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
  10. package/dist/modules/billing/api/invoices/[id]/memos/route.js +54 -0
  11. package/dist/modules/billing/api/invoices/[id]/memos/route.js.map +7 -0
  12. package/dist/modules/billing/api/invoices/[id]/pdf/route.js +69 -0
  13. package/dist/modules/billing/api/invoices/[id]/pdf/route.js.map +7 -0
  14. package/dist/modules/billing/api/invoices/[id]/route.js +44 -24
  15. package/dist/modules/billing/api/invoices/[id]/route.js.map +2 -2
  16. package/dist/modules/billing/api/invoices/[id]/send/route.js +107 -0
  17. package/dist/modules/billing/api/invoices/[id]/send/route.js.map +7 -0
  18. package/dist/modules/billing/api/invoices/add-line/route.js +2 -2
  19. package/dist/modules/billing/api/invoices/add-line/route.js.map +2 -2
  20. package/dist/modules/billing/api/invoices/delete-draft/route.js +33 -0
  21. package/dist/modules/billing/api/invoices/delete-draft/route.js.map +7 -0
  22. package/dist/modules/billing/api/invoices/edit-line/route.js +2 -2
  23. package/dist/modules/billing/api/invoices/edit-line/route.js.map +2 -2
  24. package/dist/modules/billing/api/invoices/post/route.js +2 -2
  25. package/dist/modules/billing/api/invoices/post/route.js.map +2 -2
  26. package/dist/modules/billing/api/invoices/remove-line/route.js +2 -2
  27. package/dist/modules/billing/api/invoices/remove-line/route.js.map +2 -2
  28. package/dist/modules/billing/api/invoices/route.js +45 -46
  29. package/dist/modules/billing/api/invoices/route.js.map +2 -2
  30. package/dist/modules/billing/api/items/bulk/route.js +1 -1
  31. package/dist/modules/billing/api/items/bulk/route.js.map +2 -2
  32. package/dist/modules/billing/api/items/change-price/route.js +44 -0
  33. package/dist/modules/billing/api/items/change-price/route.js.map +7 -0
  34. package/dist/modules/billing/api/items/route.js +19 -7
  35. package/dist/modules/billing/api/items/route.js.map +2 -2
  36. package/dist/modules/billing/api/payment-imports/[id]/route.js +49 -0
  37. package/dist/modules/billing/api/payment-imports/[id]/route.js.map +7 -0
  38. package/dist/modules/billing/api/payment-imports/route.js +57 -0
  39. package/dist/modules/billing/api/payment-imports/route.js.map +7 -0
  40. package/dist/modules/billing/api/payments/[id]/route.js +60 -0
  41. package/dist/modules/billing/api/payments/[id]/route.js.map +7 -0
  42. package/dist/modules/billing/api/payments/allocate/route.js +35 -0
  43. package/dist/modules/billing/api/payments/allocate/route.js.map +7 -0
  44. package/dist/modules/billing/api/payments/assign/route.js +36 -0
  45. package/dist/modules/billing/api/payments/assign/route.js.map +7 -0
  46. package/dist/modules/billing/api/payments/convert-to-topup/route.js +35 -0
  47. package/dist/modules/billing/api/payments/convert-to-topup/route.js.map +7 -0
  48. package/dist/modules/billing/api/payments/ignore/route.js +35 -0
  49. package/dist/modules/billing/api/payments/ignore/route.js.map +7 -0
  50. package/dist/modules/billing/api/payments/import/route.js +63 -0
  51. package/dist/modules/billing/api/payments/import/route.js.map +7 -0
  52. package/dist/modules/billing/api/payments/restore/route.js +35 -0
  53. package/dist/modules/billing/api/payments/restore/route.js.map +7 -0
  54. package/dist/modules/billing/api/payments/route.js +114 -0
  55. package/dist/modules/billing/api/payments/route.js.map +7 -0
  56. package/dist/modules/billing/api/payments/unallocate/route.js +35 -0
  57. package/dist/modules/billing/api/payments/unallocate/route.js.map +7 -0
  58. package/dist/modules/billing/api/payments/unassign/route.js +35 -0
  59. package/dist/modules/billing/api/payments/unassign/route.js.map +7 -0
  60. package/dist/modules/billing/api/run-outcomes/route.js +43 -6
  61. package/dist/modules/billing/api/run-outcomes/route.js.map +2 -2
  62. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js +61 -0
  63. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js.map +7 -0
  64. package/dist/modules/billing/api/runs/[id]/route.js +53 -0
  65. package/dist/modules/billing/api/runs/[id]/route.js.map +7 -0
  66. package/dist/modules/billing/api/runs/retry-failed/route.js +1 -1
  67. package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
  68. package/dist/modules/billing/api/runs/route.js +9 -3
  69. package/dist/modules/billing/api/runs/route.js.map +2 -2
  70. package/dist/modules/billing/api/settings/number-preview/route.js +42 -0
  71. package/dist/modules/billing/api/settings/number-preview/route.js.map +7 -0
  72. package/dist/modules/billing/api/settings/route.js +92 -0
  73. package/dist/modules/billing/api/settings/route.js.map +7 -0
  74. package/dist/modules/billing/api/statements/route.js +3 -0
  75. package/dist/modules/billing/api/statements/route.js.map +2 -2
  76. package/dist/modules/billing/api/test-invoices/wipe/route.js +2 -2
  77. package/dist/modules/billing/api/test-invoices/wipe/route.js.map +2 -2
  78. package/dist/modules/billing/backend/billing/accounts/[id]/page.js +121 -232
  79. package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
  80. package/dist/modules/billing/backend/billing/accounts/create/page.js +6 -0
  81. package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
  82. package/dist/modules/billing/backend/billing/accounts/page.js +63 -4
  83. package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
  84. package/dist/modules/billing/backend/billing/invoices/[id]/page.js +297 -65
  85. package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +3 -3
  86. package/dist/modules/billing/backend/billing/invoices/page.js +6 -172
  87. package/dist/modules/billing/backend/billing/invoices/page.js.map +2 -2
  88. package/dist/modules/billing/backend/billing/items/[id]/page.js +51 -7
  89. package/dist/modules/billing/backend/billing/items/[id]/page.js.map +2 -2
  90. package/dist/modules/billing/backend/billing/items/create/page.js +9 -6
  91. package/dist/modules/billing/backend/billing/items/create/page.js.map +2 -2
  92. package/dist/modules/billing/backend/billing/items/page.js +8 -2
  93. package/dist/modules/billing/backend/billing/items/page.js.map +2 -2
  94. package/dist/modules/billing/backend/billing/payments/[id]/page.js +297 -0
  95. package/dist/modules/billing/backend/billing/payments/[id]/page.js.map +7 -0
  96. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js +21 -0
  97. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js.map +7 -0
  98. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js +74 -0
  99. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js.map +7 -0
  100. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js +21 -0
  101. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js.map +7 -0
  102. package/dist/modules/billing/backend/billing/payments/page.js +58 -0
  103. package/dist/modules/billing/backend/billing/payments/page.js.map +7 -0
  104. package/dist/modules/billing/backend/billing/payments/page.meta.js +18 -0
  105. package/dist/modules/billing/backend/billing/payments/page.meta.js.map +7 -0
  106. package/dist/modules/billing/backend/billing/runs/[id]/page.js +49 -14
  107. package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +2 -2
  108. package/dist/modules/billing/backend/billing/runs/page.js +58 -7
  109. package/dist/modules/billing/backend/billing/runs/page.js.map +2 -2
  110. package/dist/modules/billing/backend/billing/statements/[id]/page.js +61 -6
  111. package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +2 -2
  112. package/dist/modules/billing/backend/billing/statements/page.js +1 -1
  113. package/dist/modules/billing/backend/billing/statements/page.js.map +2 -2
  114. package/dist/modules/billing/backend/billing/topups/page.js +1 -1
  115. package/dist/modules/billing/backend/billing/topups/page.js.map +2 -2
  116. package/dist/modules/billing/backend/billing/transactions/page.js +1 -1
  117. package/dist/modules/billing/backend/billing/transactions/page.js.map +2 -2
  118. package/dist/modules/billing/backend/billing/usage/create/page.js +3 -2
  119. package/dist/modules/billing/backend/billing/usage/create/page.js.map +2 -2
  120. package/dist/modules/billing/backend/config/billing/page.js +472 -0
  121. package/dist/modules/billing/backend/config/billing/page.js.map +7 -0
  122. package/dist/modules/billing/backend/config/billing/page.meta.js +19 -0
  123. package/dist/modules/billing/backend/config/billing/page.meta.js.map +7 -0
  124. package/dist/modules/billing/cli.js +22 -1
  125. package/dist/modules/billing/cli.js.map +2 -2
  126. package/dist/modules/billing/commands/accounts.js +6 -0
  127. package/dist/modules/billing/commands/accounts.js.map +2 -2
  128. package/dist/modules/billing/commands/index.js +3 -0
  129. package/dist/modules/billing/commands/index.js.map +2 -2
  130. package/dist/modules/billing/commands/invoices.js +269 -167
  131. package/dist/modules/billing/commands/invoices.js.map +2 -2
  132. package/dist/modules/billing/commands/items.js +108 -0
  133. package/dist/modules/billing/commands/items.js.map +2 -2
  134. package/dist/modules/billing/commands/memos.js +179 -0
  135. package/dist/modules/billing/commands/memos.js.map +7 -0
  136. package/dist/modules/billing/commands/payments.js +379 -0
  137. package/dist/modules/billing/commands/payments.js.map +7 -0
  138. package/dist/modules/billing/commands/runs.js +151 -4
  139. package/dist/modules/billing/commands/runs.js.map +2 -2
  140. package/dist/modules/billing/commands/settings.js +59 -0
  141. package/dist/modules/billing/commands/settings.js.map +7 -0
  142. package/dist/modules/billing/commands/topups.js +9 -2
  143. package/dist/modules/billing/commands/topups.js.map +2 -2
  144. package/dist/modules/billing/commands/transactions.js +2 -2
  145. package/dist/modules/billing/commands/transactions.js.map +2 -2
  146. package/dist/modules/billing/components/AccountForm.js +149 -42
  147. package/dist/modules/billing/components/AccountForm.js.map +2 -2
  148. package/dist/modules/billing/components/AccountInvoicesSection.js +64 -0
  149. package/dist/modules/billing/components/AccountInvoicesSection.js.map +7 -0
  150. package/dist/modules/billing/components/AccountPaymentsSection.js +43 -0
  151. package/dist/modules/billing/components/AccountPaymentsSection.js.map +7 -0
  152. package/dist/modules/billing/components/AccountPicker.js +56 -0
  153. package/dist/modules/billing/components/AccountPicker.js.map +7 -0
  154. package/dist/modules/billing/components/AllocatePaymentDialog.js +134 -0
  155. package/dist/modules/billing/components/AllocatePaymentDialog.js.map +7 -0
  156. package/dist/modules/billing/components/BillingDataTable.js +21 -0
  157. package/dist/modules/billing/components/BillingDataTable.js.map +7 -0
  158. package/dist/modules/billing/components/BillingSettingsLink.js +19 -0
  159. package/dist/modules/billing/components/BillingSettingsLink.js.map +7 -0
  160. package/dist/modules/billing/components/ChangeItemPriceDialog.js +158 -0
  161. package/dist/modules/billing/components/ChangeItemPriceDialog.js.map +7 -0
  162. package/dist/modules/billing/components/CreateMemoDialog.js +200 -0
  163. package/dist/modules/billing/components/CreateMemoDialog.js.map +7 -0
  164. package/dist/modules/billing/components/CustomerLabel.js +33 -0
  165. package/dist/modules/billing/components/CustomerLabel.js.map +7 -0
  166. package/dist/modules/billing/components/InvoicesTable.js +270 -0
  167. package/dist/modules/billing/components/InvoicesTable.js.map +7 -0
  168. package/dist/modules/billing/components/ItemForm.js +176 -44
  169. package/dist/modules/billing/components/ItemForm.js.map +2 -2
  170. package/dist/modules/billing/components/ItemPriceField.js +46 -0
  171. package/dist/modules/billing/components/ItemPriceField.js.map +7 -0
  172. package/dist/modules/billing/components/LineFormDialog.js +25 -2
  173. package/dist/modules/billing/components/LineFormDialog.js.map +2 -2
  174. package/dist/modules/billing/components/ManualPaymentDialog.js +122 -0
  175. package/dist/modules/billing/components/ManualPaymentDialog.js.map +7 -0
  176. package/dist/modules/billing/components/MoneyInput.js +14 -0
  177. package/dist/modules/billing/components/MoneyInput.js.map +7 -0
  178. package/dist/modules/billing/components/PaymentUploadDialog.js +115 -0
  179. package/dist/modules/billing/components/PaymentUploadDialog.js.map +7 -0
  180. package/dist/modules/billing/components/PaymentsTable.js +171 -0
  181. package/dist/modules/billing/components/PaymentsTable.js.map +7 -0
  182. package/dist/modules/billing/components/PostRunDraftsButton.js +105 -0
  183. package/dist/modules/billing/components/PostRunDraftsButton.js.map +7 -0
  184. package/dist/modules/billing/components/PrepaidPanel.js +14 -4
  185. package/dist/modules/billing/components/PrepaidPanel.js.map +2 -2
  186. package/dist/modules/billing/components/RunCyclesSection.js +209 -0
  187. package/dist/modules/billing/components/RunCyclesSection.js.map +7 -0
  188. package/dist/modules/billing/components/TestRunActions.js +127 -0
  189. package/dist/modules/billing/components/TestRunActions.js.map +7 -0
  190. package/dist/modules/billing/components/account/AccountItemsTab.js +157 -0
  191. package/dist/modules/billing/components/account/AccountItemsTab.js.map +7 -0
  192. package/dist/modules/billing/components/account/AccountRunBillingDialog.js +122 -0
  193. package/dist/modules/billing/components/account/AccountRunBillingDialog.js.map +7 -0
  194. package/dist/modules/billing/components/account/AccountRunsTab.js +152 -0
  195. package/dist/modules/billing/components/account/AccountRunsTab.js.map +7 -0
  196. package/dist/modules/billing/components/account/AccountStatementsTab.js +98 -0
  197. package/dist/modules/billing/components/account/AccountStatementsTab.js.map +7 -0
  198. package/dist/modules/billing/components/account/AccountSummaryCards.js +125 -0
  199. package/dist/modules/billing/components/account/AccountSummaryCards.js.map +7 -0
  200. package/dist/modules/billing/components/account/AccountUsageTab.js +91 -0
  201. package/dist/modules/billing/components/account/AccountUsageTab.js.map +7 -0
  202. package/dist/modules/billing/components/account/AccountView.js +73 -0
  203. package/dist/modules/billing/components/account/AccountView.js.map +7 -0
  204. package/dist/modules/billing/components/account/accountRow.js +39 -0
  205. package/dist/modules/billing/components/account/accountRow.js.map +7 -0
  206. package/dist/modules/billing/components/customerNames.js +42 -0
  207. package/dist/modules/billing/components/customerNames.js.map +7 -0
  208. package/dist/modules/billing/data/entities.js +560 -4
  209. package/dist/modules/billing/data/entities.js.map +2 -2
  210. package/dist/modules/billing/data/entityIds.js +10 -1
  211. package/dist/modules/billing/data/entityIds.js.map +2 -2
  212. package/dist/modules/billing/data/validators.js +269 -15
  213. package/dist/modules/billing/data/validators.js.map +2 -2
  214. package/dist/modules/billing/events.js +50 -0
  215. package/dist/modules/billing/events.js.map +2 -2
  216. package/dist/modules/billing/i18n/en.json +457 -21
  217. package/dist/modules/billing/i18n/pl.json +457 -21
  218. package/dist/modules/billing/index.js +6 -2
  219. package/dist/modules/billing/index.js.map +2 -2
  220. package/dist/modules/billing/lib/accountFinancials.js +58 -0
  221. package/dist/modules/billing/lib/accountFinancials.js.map +7 -0
  222. package/dist/modules/billing/lib/accountLock.js +14 -0
  223. package/dist/modules/billing/lib/accountLock.js.map +7 -0
  224. package/dist/modules/billing/lib/accountStatus.js +9 -7
  225. package/dist/modules/billing/lib/accountStatus.js.map +2 -2
  226. package/dist/modules/billing/lib/accountSummary.js +106 -0
  227. package/dist/modules/billing/lib/accountSummary.js.map +7 -0
  228. package/dist/modules/billing/lib/allocation.js +356 -0
  229. package/dist/modules/billing/lib/allocation.js.map +7 -0
  230. package/dist/modules/billing/lib/allocationEvents.js +22 -0
  231. package/dist/modules/billing/lib/allocationEvents.js.map +7 -0
  232. package/dist/modules/billing/lib/billPeriod.js +15 -1
  233. package/dist/modules/billing/lib/billPeriod.js.map +2 -2
  234. package/dist/modules/billing/lib/billRunEngine.js +237 -102
  235. package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
  236. package/dist/modules/billing/lib/billingSettings.js +116 -0
  237. package/dist/modules/billing/lib/billingSettings.js.map +7 -0
  238. package/dist/modules/billing/lib/commandRoute.js +81 -0
  239. package/dist/modules/billing/lib/commandRoute.js.map +7 -0
  240. package/dist/modules/billing/lib/customerLookup.js +97 -0
  241. package/dist/modules/billing/lib/customerLookup.js.map +7 -0
  242. package/dist/modules/billing/lib/dateOnly.js +13 -0
  243. package/dist/modules/billing/lib/dateOnly.js.map +7 -0
  244. package/dist/modules/billing/lib/documentParties.js +16 -0
  245. package/dist/modules/billing/lib/documentParties.js.map +7 -0
  246. package/dist/modules/billing/lib/documentTypes.js +27 -0
  247. package/dist/modules/billing/lib/documentTypes.js.map +7 -0
  248. package/dist/modules/billing/lib/dueDate.js +18 -0
  249. package/dist/modules/billing/lib/dueDate.js.map +7 -0
  250. package/dist/modules/billing/lib/iban.js +29 -0
  251. package/dist/modules/billing/lib/iban.js.map +7 -0
  252. package/dist/modules/billing/lib/invoiceDelivery.js +131 -0
  253. package/dist/modules/billing/lib/invoiceDelivery.js.map +7 -0
  254. package/dist/modules/billing/lib/invoiceLanguages.js +16 -0
  255. package/dist/modules/billing/lib/invoiceLanguages.js.map +7 -0
  256. package/dist/modules/billing/lib/invoiceWriter.js +107 -99
  257. package/dist/modules/billing/lib/invoiceWriter.js.map +2 -2
  258. package/dist/modules/billing/lib/itemBillingState.js +27 -0
  259. package/dist/modules/billing/lib/itemBillingState.js.map +7 -0
  260. package/dist/modules/billing/lib/itemPriceChange.js +18 -0
  261. package/dist/modules/billing/lib/itemPriceChange.js.map +7 -0
  262. package/dist/modules/billing/lib/itemSelector.js.map +2 -2
  263. package/dist/modules/billing/lib/lineAmounts.js +32 -0
  264. package/dist/modules/billing/lib/lineAmounts.js.map +7 -0
  265. package/dist/modules/billing/lib/memoGuards.js +25 -0
  266. package/dist/modules/billing/lib/memoGuards.js.map +7 -0
  267. package/dist/modules/billing/lib/navigation.js +48 -0
  268. package/dist/modules/billing/lib/navigation.js.map +7 -0
  269. package/dist/modules/billing/lib/numbering.js +120 -0
  270. package/dist/modules/billing/lib/numbering.js.map +7 -0
  271. package/dist/modules/billing/lib/overdue.js +36 -0
  272. package/dist/modules/billing/lib/overdue.js.map +7 -0
  273. package/dist/modules/billing/lib/paymentCsv.js +125 -0
  274. package/dist/modules/billing/lib/paymentCsv.js.map +7 -0
  275. package/dist/modules/billing/lib/paymentGateway.js +15 -0
  276. package/dist/modules/billing/lib/paymentGateway.js.map +7 -0
  277. package/dist/modules/billing/lib/paymentImport.js +164 -0
  278. package/dist/modules/billing/lib/paymentImport.js.map +7 -0
  279. package/dist/modules/billing/lib/paymentMatcher.js +36 -0
  280. package/dist/modules/billing/lib/paymentMatcher.js.map +7 -0
  281. package/dist/modules/billing/lib/pdf/attachmentStore.js +46 -0
  282. package/dist/modules/billing/lib/pdf/attachmentStore.js.map +7 -0
  283. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js +7 -0
  284. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js.map +7 -0
  285. package/dist/modules/billing/lib/pdf/invoiceModel.js +194 -0
  286. package/dist/modules/billing/lib/pdf/invoiceModel.js.map +7 -0
  287. package/dist/modules/billing/lib/pdf/invoicePdf.js +165 -0
  288. package/dist/modules/billing/lib/pdf/invoicePdf.js.map +7 -0
  289. package/dist/modules/billing/lib/pdf/invoiceTemplate.js +274 -0
  290. package/dist/modules/billing/lib/pdf/invoiceTemplate.js.map +7 -0
  291. package/dist/modules/billing/lib/prepaidConfig.js +17 -25
  292. package/dist/modules/billing/lib/prepaidConfig.js.map +2 -2
  293. package/dist/modules/billing/lib/prepaidConsume.js +26 -7
  294. package/dist/modules/billing/lib/prepaidConsume.js.map +2 -2
  295. package/dist/modules/billing/lib/prepaidPeriodClose.js +277 -158
  296. package/dist/modules/billing/lib/prepaidPeriodClose.js.map +2 -2
  297. package/dist/modules/billing/lib/prepaidTaxPoint.js +37 -0
  298. package/dist/modules/billing/lib/prepaidTaxPoint.js.map +7 -0
  299. package/dist/modules/billing/lib/seeds/demo.js +51 -3
  300. package/dist/modules/billing/lib/seeds/demo.js.map +2 -2
  301. package/dist/modules/billing/lib/seeds/paymentsSample.js +93 -0
  302. package/dist/modules/billing/lib/seeds/paymentsSample.js.map +7 -0
  303. package/dist/modules/billing/lib/seeds/sellerSettings.js +41 -0
  304. package/dist/modules/billing/lib/seeds/sellerSettings.js.map +7 -0
  305. package/dist/modules/billing/lib/servicePeriods.js +121 -0
  306. package/dist/modules/billing/lib/servicePeriods.js.map +7 -0
  307. package/dist/modules/billing/lib/testRunCleanup.js +24 -0
  308. package/dist/modules/billing/lib/testRunCleanup.js.map +7 -0
  309. package/dist/modules/billing/lib/topupCapture.js +2 -2
  310. package/dist/modules/billing/lib/topupCapture.js.map +2 -2
  311. package/dist/modules/billing/lib/topupReceipt.js +58 -79
  312. package/dist/modules/billing/lib/topupReceipt.js.map +2 -2
  313. package/dist/modules/billing/lib/usageRater.js.map +1 -1
  314. package/dist/modules/billing/lib/usageRunner.js +14 -6
  315. package/dist/modules/billing/lib/usageRunner.js.map +2 -2
  316. package/dist/modules/billing/lib/vat.js +39 -0
  317. package/dist/modules/billing/lib/vat.js.map +7 -0
  318. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js +15 -0
  319. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js.map +7 -0
  320. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js +25 -0
  321. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js.map +7 -0
  322. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js +115 -0
  323. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js.map +7 -0
  324. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js +47 -0
  325. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js.map +7 -0
  326. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js +89 -0
  327. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js.map +7 -0
  328. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js +17 -0
  329. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js.map +7 -0
  330. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js +27 -0
  331. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js.map +7 -0
  332. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js +29 -0
  333. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js.map +7 -0
  334. package/dist/modules/billing/setup.js +7 -184
  335. package/dist/modules/billing/setup.js.map +2 -2
  336. package/dist/modules/billing/subscribers/credit-memo-auto-send.js +16 -0
  337. package/dist/modules/billing/subscribers/credit-memo-auto-send.js.map +7 -0
  338. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js +16 -0
  339. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js.map +7 -0
  340. package/dist/modules/billing/subscribers/debit-memo-auto-send.js +16 -0
  341. package/dist/modules/billing/subscribers/debit-memo-auto-send.js.map +7 -0
  342. package/dist/modules/billing/subscribers/invoice-apply-credit.js +34 -0
  343. package/dist/modules/billing/subscribers/invoice-apply-credit.js.map +7 -0
  344. package/dist/modules/billing/subscribers/invoice-auto-send.js +30 -0
  345. package/dist/modules/billing/subscribers/invoice-auto-send.js.map +7 -0
  346. package/dist/modules/billing/subscribers/payment-captured-to-paid.js +69 -33
  347. package/dist/modules/billing/subscribers/payment-captured-to-paid.js.map +2 -2
  348. package/dist/modules/billing/subscribers/prepaid-topup-captured.js +4 -6
  349. package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +2 -2
  350. package/generated/entities/billing_account/index.ts +3 -0
  351. package/generated/entities/billing_allocation/index.ts +18 -0
  352. package/generated/entities/billing_document_sequence/index.ts +11 -0
  353. package/generated/entities/billing_invoice/index.ts +38 -0
  354. package/generated/entities/billing_invoice_line/index.ts +24 -0
  355. package/generated/entities/billing_item/index.ts +4 -0
  356. package/generated/entities/billing_payment/index.ts +25 -0
  357. package/generated/entities/billing_payment_import/index.ts +14 -0
  358. package/generated/entities/billing_run/index.ts +0 -1
  359. package/generated/entities/billing_run_outcome/index.ts +1 -0
  360. package/generated/entities/billing_settings/index.ts +26 -0
  361. package/generated/entities/billing_statement/index.ts +1 -0
  362. package/generated/entities.ids.generated.ts +7 -0
  363. package/package.json +5 -1
  364. package/src/modules/billing/__integration__/TC-BILL-004.spec.ts +170 -0
  365. package/src/modules/billing/__integration__/TC-BILL-005.spec.ts +134 -0
  366. package/src/modules/billing/__integration__/TC-BILL-006.spec.ts +101 -0
  367. package/src/modules/billing/__integration__/TC-BILL-007.spec.ts +140 -0
  368. package/src/modules/billing/__integration__/TC-BILL-008.spec.ts +108 -0
  369. package/src/modules/billing/__integration__/TC-BILL-009.spec.ts +113 -0
  370. package/src/modules/billing/__integration__/TC-BILL-010.spec.ts +118 -0
  371. package/src/modules/billing/__integration__/TC-BILL-011.spec.ts +142 -0
  372. package/src/modules/billing/__integration__/TC-BILL-012.spec.ts +115 -0
  373. package/src/modules/billing/__integration__/TC-BILL-013.spec.ts +118 -0
  374. package/src/modules/billing/__integration__/sellerSettings.ts +24 -0
  375. package/src/modules/billing/__tests__/commands/invoices.test.ts +436 -354
  376. package/src/modules/billing/__tests__/commands/itemsBulk.test.ts +33 -0
  377. package/src/modules/billing/__tests__/commands/memos.test.ts +205 -0
  378. package/src/modules/billing/__tests__/commands/payments.test.ts +241 -0
  379. package/src/modules/billing/__tests__/commands/runsDelete.test.ts +146 -0
  380. package/src/modules/billing/__tests__/commands/runsPostDrafts.test.ts +106 -0
  381. package/src/modules/billing/__tests__/commands/topups.test.ts +11 -0
  382. package/src/modules/billing/__tests__/entityRegistry.test.ts +8 -1
  383. package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +28 -11
  384. package/src/modules/billing/__tests__/lib/accountFinancials.test.ts +53 -0
  385. package/src/modules/billing/__tests__/lib/accountSummary.test.ts +97 -0
  386. package/src/modules/billing/__tests__/lib/allocation.test.ts +319 -0
  387. package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +228 -17
  388. package/src/modules/billing/__tests__/lib/billingSettings.test.ts +109 -0
  389. package/src/modules/billing/__tests__/lib/customerLookup.test.ts +156 -0
  390. package/src/modules/billing/__tests__/lib/dueDate.test.ts +35 -0
  391. package/src/modules/billing/__tests__/lib/iban.test.ts +38 -0
  392. package/src/modules/billing/__tests__/lib/invoiceDelivery.test.ts +189 -0
  393. package/src/modules/billing/__tests__/lib/invoiceWriter.test.ts +212 -10
  394. package/src/modules/billing/__tests__/lib/itemBillingState.test.ts +42 -0
  395. package/src/modules/billing/__tests__/lib/itemPriceChange.test.ts +165 -0
  396. package/src/modules/billing/__tests__/lib/lineAmounts.test.ts +86 -0
  397. package/src/modules/billing/__tests__/lib/navigation.test.ts +71 -0
  398. package/src/modules/billing/__tests__/lib/numbering.test.ts +91 -0
  399. package/src/modules/billing/__tests__/lib/overdue.test.ts +36 -0
  400. package/src/modules/billing/__tests__/lib/paymentCsv.test.ts +148 -0
  401. package/src/modules/billing/__tests__/lib/paymentImport.test.ts +113 -0
  402. package/src/modules/billing/__tests__/lib/paymentsSample.test.ts +73 -0
  403. package/src/modules/billing/__tests__/lib/pdf/invoicePdf.test.ts +199 -0
  404. package/src/modules/billing/__tests__/lib/pdf/invoicePdfService.test.ts +147 -0
  405. package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +374 -98
  406. package/src/modules/billing/__tests__/lib/prepaidTaxPoint.test.ts +118 -0
  407. package/src/modules/billing/__tests__/lib/seedsDemo.test.ts +53 -2
  408. package/src/modules/billing/__tests__/lib/sellerSettings.test.ts +28 -0
  409. package/src/modules/billing/__tests__/lib/servicePeriods.test.ts +190 -0
  410. package/src/modules/billing/__tests__/lib/topupCapture.test.ts +6 -28
  411. package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +21 -52
  412. package/src/modules/billing/__tests__/lib/vat.test.ts +51 -0
  413. package/src/modules/billing/__tests__/package-isolation.test.ts +22 -8
  414. package/src/modules/billing/__tests__/setup.test.ts +13 -4
  415. package/src/modules/billing/__tests__/validators.test.ts +193 -3
  416. package/src/modules/billing/acl.ts +12 -0
  417. package/src/modules/billing/api/accounts/[id]/summary/route.ts +62 -0
  418. package/src/modules/billing/api/accounts/route.ts +58 -4
  419. package/src/modules/billing/api/export/account/[id]/route.ts +13 -11
  420. package/src/modules/billing/api/invoices/[id]/memos/route.ts +63 -0
  421. package/src/modules/billing/api/invoices/[id]/pdf/route.ts +79 -0
  422. package/src/modules/billing/api/invoices/[id]/route.ts +57 -70
  423. package/src/modules/billing/api/invoices/[id]/send/route.ts +127 -0
  424. package/src/modules/billing/api/invoices/add-line/route.ts +2 -2
  425. package/src/modules/billing/api/invoices/delete-draft/route.ts +33 -0
  426. package/src/modules/billing/api/invoices/edit-line/route.ts +2 -2
  427. package/src/modules/billing/api/invoices/post/route.ts +7 -6
  428. package/src/modules/billing/api/invoices/remove-line/route.ts +2 -2
  429. package/src/modules/billing/api/invoices/route.ts +81 -86
  430. package/src/modules/billing/api/items/bulk/route.ts +2 -1
  431. package/src/modules/billing/api/items/change-price/route.ts +54 -0
  432. package/src/modules/billing/api/items/route.ts +23 -7
  433. package/src/modules/billing/api/payment-imports/[id]/route.ts +49 -0
  434. package/src/modules/billing/api/payment-imports/route.ts +57 -0
  435. package/src/modules/billing/api/payments/[id]/route.ts +63 -0
  436. package/src/modules/billing/api/payments/allocate/route.ts +35 -0
  437. package/src/modules/billing/api/payments/assign/route.ts +36 -0
  438. package/src/modules/billing/api/payments/convert-to-topup/route.ts +35 -0
  439. package/src/modules/billing/api/payments/ignore/route.ts +35 -0
  440. package/src/modules/billing/api/payments/import/route.ts +72 -0
  441. package/src/modules/billing/api/payments/restore/route.ts +35 -0
  442. package/src/modules/billing/api/payments/route.ts +147 -0
  443. package/src/modules/billing/api/payments/unallocate/route.ts +35 -0
  444. package/src/modules/billing/api/payments/unassign/route.ts +35 -0
  445. package/src/modules/billing/api/run-outcomes/route.ts +49 -6
  446. package/src/modules/billing/api/runs/[id]/post-drafts/route.ts +71 -0
  447. package/src/modules/billing/api/runs/[id]/route.ts +59 -0
  448. package/src/modules/billing/api/runs/retry-failed/route.ts +2 -1
  449. package/src/modules/billing/api/runs/route.ts +15 -7
  450. package/src/modules/billing/api/settings/number-preview/route.ts +46 -0
  451. package/src/modules/billing/api/settings/route.ts +116 -0
  452. package/src/modules/billing/api/statements/route.ts +5 -0
  453. package/src/modules/billing/api/test-invoices/wipe/route.ts +2 -2
  454. package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +151 -372
  455. package/src/modules/billing/backend/billing/accounts/create/page.tsx +6 -0
  456. package/src/modules/billing/backend/billing/accounts/page.tsx +70 -4
  457. package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +444 -95
  458. package/src/modules/billing/backend/billing/invoices/page.tsx +8 -227
  459. package/src/modules/billing/backend/billing/items/[id]/page.tsx +75 -6
  460. package/src/modules/billing/backend/billing/items/create/page.tsx +13 -8
  461. package/src/modules/billing/backend/billing/items/page.tsx +18 -2
  462. package/src/modules/billing/backend/billing/payments/[id]/page.meta.ts +19 -0
  463. package/src/modules/billing/backend/billing/payments/[id]/page.tsx +415 -0
  464. package/src/modules/billing/backend/billing/payments/imports/[id]/page.meta.ts +19 -0
  465. package/src/modules/billing/backend/billing/payments/imports/[id]/page.tsx +106 -0
  466. package/src/modules/billing/backend/billing/payments/page.meta.ts +16 -0
  467. package/src/modules/billing/backend/billing/payments/page.tsx +56 -0
  468. package/src/modules/billing/backend/billing/runs/[id]/page.tsx +49 -18
  469. package/src/modules/billing/backend/billing/runs/page.tsx +59 -8
  470. package/src/modules/billing/backend/billing/statements/[id]/page.tsx +86 -5
  471. package/src/modules/billing/backend/billing/statements/page.tsx +1 -1
  472. package/src/modules/billing/backend/billing/topups/page.tsx +1 -1
  473. package/src/modules/billing/backend/billing/transactions/page.tsx +1 -1
  474. package/src/modules/billing/backend/billing/usage/create/page.tsx +3 -2
  475. package/src/modules/billing/backend/config/billing/page.meta.ts +21 -0
  476. package/src/modules/billing/backend/config/billing/page.tsx +549 -0
  477. package/src/modules/billing/cli.ts +31 -1
  478. package/src/modules/billing/commands/accounts.ts +6 -0
  479. package/src/modules/billing/commands/index.ts +3 -0
  480. package/src/modules/billing/commands/invoices.ts +364 -231
  481. package/src/modules/billing/commands/items.ts +143 -0
  482. package/src/modules/billing/commands/memos.ts +218 -0
  483. package/src/modules/billing/commands/payments.ts +428 -0
  484. package/src/modules/billing/commands/runs.ts +205 -3
  485. package/src/modules/billing/commands/settings.ts +77 -0
  486. package/src/modules/billing/commands/topups.ts +12 -3
  487. package/src/modules/billing/commands/transactions.ts +2 -2
  488. package/src/modules/billing/components/AccountForm.tsx +199 -53
  489. package/src/modules/billing/components/AccountInvoicesSection.tsx +75 -0
  490. package/src/modules/billing/components/AccountPaymentsSection.tsx +42 -0
  491. package/src/modules/billing/components/AccountPicker.tsx +70 -0
  492. package/src/modules/billing/components/AllocatePaymentDialog.tsx +167 -0
  493. package/src/modules/billing/components/BillingDataTable.tsx +34 -0
  494. package/src/modules/billing/components/BillingSettingsLink.tsx +25 -0
  495. package/src/modules/billing/components/ChangeItemPriceDialog.tsx +180 -0
  496. package/src/modules/billing/components/CreateMemoDialog.tsx +232 -0
  497. package/src/modules/billing/components/CustomerLabel.tsx +53 -0
  498. package/src/modules/billing/components/InvoicesTable.tsx +344 -0
  499. package/src/modules/billing/components/ItemForm.tsx +233 -44
  500. package/src/modules/billing/components/ItemPriceField.tsx +81 -0
  501. package/src/modules/billing/components/LineFormDialog.tsx +25 -1
  502. package/src/modules/billing/components/ManualPaymentDialog.tsx +140 -0
  503. package/src/modules/billing/components/MoneyInput.tsx +26 -0
  504. package/src/modules/billing/components/PaymentUploadDialog.tsx +148 -0
  505. package/src/modules/billing/components/PaymentsTable.tsx +213 -0
  506. package/src/modules/billing/components/PostRunDraftsButton.tsx +129 -0
  507. package/src/modules/billing/components/PrepaidPanel.tsx +19 -6
  508. package/src/modules/billing/components/RunCyclesSection.tsx +298 -0
  509. package/src/modules/billing/components/TestRunActions.tsx +152 -0
  510. package/src/modules/billing/components/account/AccountItemsTab.tsx +192 -0
  511. package/src/modules/billing/components/account/AccountRunBillingDialog.tsx +147 -0
  512. package/src/modules/billing/components/account/AccountRunsTab.tsx +201 -0
  513. package/src/modules/billing/components/account/AccountStatementsTab.tsx +120 -0
  514. package/src/modules/billing/components/account/AccountSummaryCards.tsx +153 -0
  515. package/src/modules/billing/components/account/AccountUsageTab.tsx +122 -0
  516. package/src/modules/billing/components/account/AccountView.tsx +101 -0
  517. package/src/modules/billing/components/account/accountRow.ts +70 -0
  518. package/src/modules/billing/components/customerNames.ts +62 -0
  519. package/src/modules/billing/data/entities.ts +654 -5
  520. package/src/modules/billing/data/entityIds.ts +9 -0
  521. package/src/modules/billing/data/validators.ts +388 -8
  522. package/src/modules/billing/events.ts +56 -3
  523. package/src/modules/billing/i18n/en.json +457 -21
  524. package/src/modules/billing/i18n/pl.json +457 -21
  525. package/src/modules/billing/index.ts +6 -2
  526. package/src/modules/billing/lib/accountFinancials.ts +84 -0
  527. package/src/modules/billing/lib/accountLock.ts +32 -0
  528. package/src/modules/billing/lib/accountStatus.ts +11 -9
  529. package/src/modules/billing/lib/accountSummary.ts +172 -0
  530. package/src/modules/billing/lib/allocation.ts +515 -0
  531. package/src/modules/billing/lib/allocationEvents.ts +26 -0
  532. package/src/modules/billing/lib/billPeriod.ts +19 -0
  533. package/src/modules/billing/lib/billRunEngine.ts +350 -150
  534. package/src/modules/billing/lib/billingSettings.ts +145 -0
  535. package/src/modules/billing/lib/commandRoute.ts +112 -0
  536. package/src/modules/billing/lib/customerLookup.ts +157 -0
  537. package/src/modules/billing/lib/dateOnly.ts +22 -0
  538. package/src/modules/billing/lib/documentParties.ts +23 -0
  539. package/src/modules/billing/lib/documentTypes.ts +37 -0
  540. package/src/modules/billing/lib/dueDate.ts +26 -0
  541. package/src/modules/billing/lib/iban.ts +36 -0
  542. package/src/modules/billing/lib/invoiceDelivery.ts +197 -0
  543. package/src/modules/billing/lib/invoiceLanguages.ts +34 -0
  544. package/src/modules/billing/lib/invoiceWriter.ts +162 -148
  545. package/src/modules/billing/lib/itemBillingState.ts +67 -0
  546. package/src/modules/billing/lib/itemPriceChange.ts +56 -0
  547. package/src/modules/billing/lib/itemSelector.ts +4 -1
  548. package/src/modules/billing/lib/lineAmounts.ts +61 -0
  549. package/src/modules/billing/lib/memoGuards.ts +41 -0
  550. package/src/modules/billing/lib/navigation.ts +64 -0
  551. package/src/modules/billing/lib/numbering.ts +174 -0
  552. package/src/modules/billing/lib/overdue.ts +60 -0
  553. package/src/modules/billing/lib/paymentCsv.ts +171 -0
  554. package/src/modules/billing/lib/paymentGateway.ts +52 -0
  555. package/src/modules/billing/lib/paymentImport.ts +228 -0
  556. package/src/modules/billing/lib/paymentMatcher.ts +88 -0
  557. package/src/modules/billing/lib/pdf/attachmentStore.ts +60 -0
  558. package/src/modules/billing/lib/pdf/fonts/NotoSans-Bold.ttf +0 -0
  559. package/src/modules/billing/lib/pdf/fonts/NotoSans-Regular.ttf +0 -0
  560. package/src/modules/billing/lib/pdf/fonts/OFL.txt +93 -0
  561. package/src/modules/billing/lib/pdf/fonts/embeddedFonts.ts +9 -0
  562. package/src/modules/billing/lib/pdf/invoiceModel.ts +328 -0
  563. package/src/modules/billing/lib/pdf/invoicePdf.ts +216 -0
  564. package/src/modules/billing/lib/pdf/invoiceTemplate.ts +338 -0
  565. package/src/modules/billing/lib/prepaidConfig.ts +27 -36
  566. package/src/modules/billing/lib/prepaidConsume.ts +29 -6
  567. package/src/modules/billing/lib/prepaidPeriodClose.ts +407 -239
  568. package/src/modules/billing/lib/prepaidTaxPoint.ts +76 -0
  569. package/src/modules/billing/lib/seeds/demo.ts +79 -6
  570. package/src/modules/billing/lib/seeds/paymentsSample.ts +136 -0
  571. package/src/modules/billing/lib/seeds/sellerSettings.ts +78 -0
  572. package/src/modules/billing/lib/servicePeriods.ts +226 -0
  573. package/src/modules/billing/lib/testRunCleanup.ts +42 -0
  574. package/src/modules/billing/lib/topupCapture.ts +3 -4
  575. package/src/modules/billing/lib/topupReceipt.ts +70 -103
  576. package/src/modules/billing/lib/usageRater.ts +1 -1
  577. package/src/modules/billing/lib/usageRunner.ts +17 -5
  578. package/src/modules/billing/lib/vat.ts +56 -0
  579. package/src/modules/billing/migrations/Migration20261114000000_billing_customer_kind.ts +25 -0
  580. package/src/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.ts +37 -0
  581. package/src/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.ts +132 -0
  582. package/src/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.ts +58 -0
  583. package/src/modules/billing/migrations/Migration20261117000000_billing_payments.ts +97 -0
  584. package/src/modules/billing/migrations/Migration20261118000000_billing_memos.ts +21 -0
  585. package/src/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.ts +39 -0
  586. package/src/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.ts +35 -0
  587. package/src/modules/billing/setup.ts +11 -237
  588. package/src/modules/billing/subscribers/credit-memo-auto-send.ts +15 -0
  589. package/src/modules/billing/subscribers/debit-memo-apply-credit.ts +15 -0
  590. package/src/modules/billing/subscribers/debit-memo-auto-send.ts +15 -0
  591. package/src/modules/billing/subscribers/invoice-apply-credit.ts +39 -0
  592. package/src/modules/billing/subscribers/invoice-auto-send.ts +46 -0
  593. package/src/modules/billing/subscribers/payment-captured-to-paid.ts +84 -62
  594. package/src/modules/billing/subscribers/prepaid-topup-captured.ts +4 -8
  595. package/dist/modules/billing/lib/invoiceStatus.js +0 -38
  596. package/dist/modules/billing/lib/invoiceStatus.js.map +0 -7
  597. package/src/modules/billing/__tests__/lib/invoiceStatus.test.ts +0 -97
  598. package/src/modules/billing/lib/invoiceStatus.ts +0 -61
@@ -8,43 +8,97 @@ import {
8
8
  BillingRun,
9
9
  } from '../data/entities'
10
10
  import type { BillCycle } from '../data/validators'
11
- import { advanceNextBillDate, addDays, deriveBillPeriod, isCycleDue, type BillPeriod } from './billPeriod'
11
+ import {
12
+ addDays,
13
+ advanceNextBillDate,
14
+ deriveBillPeriod,
15
+ isCycleDue,
16
+ shiftCycles,
17
+ type BillPeriod,
18
+ } from './billPeriod'
12
19
  import { applyBalanceMovement } from './balanceLedger'
13
20
  import { resolveLowBalanceThreshold } from './balanceStatus'
14
- import { buildInvoiceLinesFromItems } from './invoiceWriter'
15
- import { selectItemsForPeriod, type SelectableItem } from './itemSelector'
21
+ import {
22
+ buildInvoiceLinesFromItems,
23
+ buildRecurringLineDescriptors,
24
+ type InvoiceLineDescriptor,
25
+ } from './invoiceWriter'
26
+ import { shouldIncludeItem, type SelectableItem } from './itemSelector'
27
+ import { computeRecurringLines, type RecurringSkip } from './servicePeriods'
16
28
  import { absMoney, addMoney, fromUnits, subMoney, toUnits } from './money'
17
29
  import {
18
30
  getLowBalanceThresholdDefault,
19
31
  getNearLimitBufferDefault,
32
+ getPrepaidTaxPoint,
20
33
  getStatementEnabled,
21
34
  } from './prepaidConfig'
35
+ import {
36
+ buildVatSummary,
37
+ resolvePrepaidDebit,
38
+ type PrepaidDebit,
39
+ type PrepaidTaxPoint,
40
+ type VatSummaryRow,
41
+ } from './prepaidTaxPoint'
22
42
  import { buildBalanceCrossingEvents, type DomainEvent } from './balanceEvents'
43
+ import { toIsoDate, toUtcMidnight } from './dateOnly'
23
44
 
24
45
  /**
25
- * Prepaid period close (SPEC-002 P3).
46
+ * Prepaid period close (SPEC-002 P3, reworked by Spec F).
47
+ *
48
+ * A prepaid account never gets a payable draft invoice. For every due bill
49
+ * date D (catch-up up to `asOfDate`, one rule for every trigger) the run:
50
+ * 1. charges `recurring` items per their `charge_timing` and `prorate`
51
+ * (Spec F, F-D4 — the same `computeRecurringLines` the postpaid engine
52
+ * uses: in advance → `[D, D + cycle − 1]`, in arrears → `[D − cycle,
53
+ * D − 1]`) and `one_time` items on the first bill date whose window
54
+ * overlaps them, debiting the balance through the org's tax point
55
+ * (Spec F, F-D3 — `resolvePrepaidDebit`);
56
+ * 2. builds a non-fiscal **BillingStatement** for `[D − cycle, D − 1]`
57
+ * (opening, top-ups, usage, charges taken at D, closing, per-line
58
+ * breakdown, VAT summary).
26
59
  *
27
- * A prepaid account never gets a payable draft invoice. Instead the Bill Run,
28
- * for a due prepaid account:
29
- * 1. charges its `recurring` / `one_time` items to the **balance** (atomic
30
- * debit + transaction, deterministic `source_ref =
31
- * recurring-{itemId}-{periodEndISO}`, then `billed_to_date = period_end`),
32
- * 2. builds a non-fiscal **BillingStatement** (opening, topups, usage,
33
- * recurring, closing + per-item breakdown).
60
+ * Metered usage was rated + debited in real time during the period — it is
61
+ * NOT re-rated here; the statement only summarises it.
34
62
  *
35
- * Metered usage was already rated + debited in real time during the period —
36
- * it is NOT re-rated here; the statement only summarizes it.
63
+ * Bill date and item watermarks advance in memory, so test and dry runs
64
+ * simulate every due cycle exactly like a real run; only persistence differs.
37
65
  *
38
- * Three anti-duplicate guards: the statement unique
39
- * `(bill_account_id, period_start, period_end)`, the per-item `billed_to_date`,
40
- * and the deterministic charge `source_ref`.
66
+ * Anti-duplicate guards: the per-item `billed_to_date` watermark, the
67
+ * deterministic charge `source_ref`, and the statement unique
68
+ * `(bill_account_id, period_start, period_end)`. A statement that already
69
+ * exists for a period means that period was closed: the run skips the
70
+ * statement, still applies (idempotent) charges, and advances — it never
71
+ * leaves the account stuck behind an existing statement.
41
72
  */
42
73
 
43
74
  export type PrepaidCloseMode = 'real' | 'test' | 'dry'
44
75
 
76
+ export type PrepaidCycleStatus = 'charged' | 'previewed' | 'empty' | 'skipped_existing'
77
+
78
+ /** Same shape as the postpaid `CycleRecord` (Spec F, P-D6) plus prepaid fields. */
79
+ export type PrepaidCycleRecord = {
80
+ bill_date: string
81
+ period_start: string
82
+ period_end: string
83
+ status: PrepaidCycleStatus
84
+ line_count: number
85
+ total_net: string
86
+ /** What left the balance for this bill date (gross in `consumption` mode). */
87
+ total_debit: string
88
+ invoice_id: null
89
+ statement_id: string | null
90
+ }
91
+
92
+ export type PrepaidSkippedItem = {
93
+ item_id: string
94
+ reason: string
95
+ service_period_start: string | null
96
+ service_period_end: string | null
97
+ }
98
+
45
99
  export type PrepaidAccountWarnings = {
46
- // Reserved for future per-close warnings (e.g. negative-balance close).
47
- closed_negative?: boolean
100
+ skipped_items?: PrepaidSkippedItem[]
101
+ catch_up_cap_reached?: boolean
48
102
  }
49
103
 
50
104
  export type PrepaidAccountResult =
@@ -53,279 +107,362 @@ export type PrepaidAccountResult =
53
107
  warnings: PrepaidAccountWarnings | null
54
108
  statementId: string | null
55
109
  cyclesEmitted: number
110
+ cycles: PrepaidCycleRecord[]
56
111
  statementEvents: DomainEvent[]
57
112
  }
58
113
  | { status: 'failed'; errorMessage: string }
59
114
 
60
- const SCHEDULED_CYCLE_LIMIT = 1
61
115
  const CATCH_UP_HARD_CAP = 60
116
+ const SKIPPED_ITEMS_WARNING_CAP = 200
117
+ /** Skip reasons that are normal state, not worth surfacing on the outcome. */
118
+ const QUIET_SKIP_REASONS = new Set(['item-inactive', 'item-soft-deleted'])
62
119
 
63
120
  export type ProcessPrepaidParams = {
64
121
  asOfDate: Date
65
122
  mode: PrepaidCloseMode
66
123
  triggeredBy: 'schedule' | 'manual'
67
- catchUp?: boolean
68
124
  }
69
125
 
70
- /**
71
- * Multi-cycle prepaid account processing (mirrors the postpaid engine's
72
- * catch-up loop). One statement + one set of recurring debits per missed
73
- * period — never combined.
74
- */
126
+ type ChargeLine = {
127
+ descriptor: InvoiceLineDescriptor
128
+ amounts: PrepaidDebit
129
+ sourceRef: string
130
+ }
131
+
132
+ type CycleContext = {
133
+ mode: PrepaidCloseMode
134
+ taxPoint: PrepaidTaxPoint
135
+ statementEnabled: boolean
136
+ threshold: string
137
+ nearLimitBuffer: string
138
+ creditLimit: string
139
+ }
140
+
141
+ function chargeSourceRef(line: InvoiceLineDescriptor, kind: 'charge' | 'credit'): string {
142
+ if (line.billingType === 'one_time') return `one_time-${line.billingItemId}`
143
+ const start = line.servicePeriodStart ? toIsoDate(line.servicePeriodStart) : 'na'
144
+ const end = line.servicePeriodEnd ? toIsoDate(line.servicePeriodEnd) : 'na'
145
+ return `recurring-${line.billingItemId}-${kind}-${start}-${end}`
146
+ }
147
+
148
+ function money4(value: number): string {
149
+ return fromUnits(Math.round(value * 10000))
150
+ }
151
+
152
+ function toSkipWarning(skip: RecurringSkip): PrepaidSkippedItem {
153
+ return {
154
+ item_id: skip.billingItemId,
155
+ reason: skip.reason,
156
+ service_period_start: skip.servicePeriodStart ? toIsoDate(skip.servicePeriodStart) : null,
157
+ service_period_end: skip.servicePeriodEnd ? toIsoDate(skip.servicePeriodEnd) : null,
158
+ }
159
+ }
160
+
161
+ function toCharge(descriptor: InvoiceLineDescriptor, kind: 'charge' | 'credit', taxPoint: PrepaidTaxPoint): ChargeLine {
162
+ return {
163
+ descriptor,
164
+ amounts: resolvePrepaidDebit({
165
+ lineAmount: descriptor.lineAmount,
166
+ priceBasis: descriptor.priceBasis,
167
+ taxRate: descriptor.taxRate,
168
+ taxPoint,
169
+ }),
170
+ sourceRef: chargeSourceRef(descriptor, kind),
171
+ }
172
+ }
173
+
75
174
  export async function processPrepaidAccount(
76
175
  em: EntityManager,
77
176
  account: BillingAccount,
78
177
  billRun: BillingRun,
79
178
  params: ProcessPrepaidParams,
80
179
  ): Promise<PrepaidAccountResult> {
81
- const maxCycles =
82
- params.triggeredBy === 'schedule' || !params.catchUp
83
- ? SCHEDULED_CYCLE_LIMIT
84
- : CATCH_UP_HARD_CAP
85
-
86
- const statementEnabled = await getStatementEnabled(em)
87
- // Resolve the crossing thresholds once — they don't change during the run.
180
+ const scope = { tenantId: account.tenantId, organizationId: account.organizationId }
181
+ const cycle = account.billCycle as BillCycle
88
182
  const balanceRow = await em.findOne(BillingAccountBalance, {
89
183
  tenantId: account.tenantId,
90
184
  billAccountId: account.id,
91
185
  })
92
- const threshold = resolveLowBalanceThreshold(
93
- balanceRow?.lowBalanceThreshold ?? null,
94
- await getLowBalanceThresholdDefault(em),
186
+ const ctx: CycleContext = {
187
+ mode: params.mode,
188
+ taxPoint: await getPrepaidTaxPoint(em, scope),
189
+ statementEnabled: await getStatementEnabled(em, scope),
190
+ threshold: resolveLowBalanceThreshold(
191
+ balanceRow?.lowBalanceThreshold ?? null,
192
+ await getLowBalanceThresholdDefault(em, scope),
193
+ ),
194
+ nearLimitBuffer: await getNearLimitBufferDefault(em, scope),
195
+ creditLimit: account.creditLimit ?? '0',
196
+ }
197
+
198
+ const items = (await em.find(BillingItem, {
199
+ tenantId: account.tenantId,
200
+ organizationId: account.organizationId,
201
+ billAccountId: account.id,
202
+ deletedAt: null,
203
+ } as never)) as BillingItem[]
204
+ const recurringItems = items.filter((it) => it.type === 'recurring')
205
+ const oneTimeItems = items.filter((it) => it.type === 'one_time')
206
+
207
+ // In-memory cycle state — persisted per cycle only in real mode.
208
+ const watermarks = new Map<string, Date | null>(
209
+ recurringItems.map((it) => [it.id, it.billedToDate ? toUtcMidnight(it.billedToDate) : null]),
95
210
  )
96
- const nearLimitBuffer = await getNearLimitBufferDefault(em)
97
- const creditLimit = account.creditLimit ?? '0'
211
+ const billedOneTimes = new Set<string>(oneTimeItems.filter((it) => it.billedToDate).map((it) => it.id))
212
+ let billDate = toUtcMidnight(account.nextBillDate)
213
+ // Chain opening → closing across cycles. Null on the first cycle →
214
+ // resolved from the prior statement / ledger.
215
+ let previousClosing: string | null = null
98
216
 
99
- let cyclesEmitted = 0
100
- let lastStatementId: string | null = null
217
+ const cycles: PrepaidCycleRecord[] = []
218
+ const skippedItems: PrepaidSkippedItem[] = []
219
+ const warnings: PrepaidAccountWarnings = {}
101
220
  const statementEvents: DomainEvent[] = []
102
- // Chain opening → closing across cycles in a catch-up run. Null on the first
103
- // cycle → closePrepaidPeriod resolves it from the prior statement / ledger.
104
- let previousClosing: string | null = null
221
+ let lastStatementId: string | null = null
105
222
 
106
- while (cyclesEmitted < maxCycles && isCycleDue(account.nextBillDate, params.asOfDate)) {
107
- const period = deriveBillPeriod(account.nextBillDate, account.billCycle as BillCycle)
223
+ const pushSkips = (skips: readonly RecurringSkip[]) => {
224
+ for (const skip of skips) {
225
+ if (QUIET_SKIP_REASONS.has(skip.reason)) continue
226
+ if (skippedItems.length < SKIPPED_ITEMS_WARNING_CAP) skippedItems.push(toSkipWarning(skip))
227
+ }
228
+ }
108
229
 
109
- // Statement anti-duplicate (only when we would actually persist).
110
- if (params.mode !== 'dry') {
111
- const existing = await em.findOne(BillingStatement, {
112
- tenantId: account.tenantId,
113
- billAccountId: account.id,
114
- periodStart: period.periodStart,
115
- periodEnd: period.periodEnd,
116
- })
117
- if (existing) {
118
- return {
119
- status: 'skipped_existing_statement',
120
- warnings: null,
121
- statementId: existing.id,
122
- cyclesEmitted,
123
- statementEvents,
124
- }
125
- }
230
+ while (isCycleDue(billDate, params.asOfDate)) {
231
+ if (cycles.length >= CATCH_UP_HARD_CAP) {
232
+ warnings.catch_up_cap_reached = true
233
+ break
126
234
  }
235
+ const nextBillDate = advanceNextBillDate(billDate, cycle)
236
+ const period = deriveBillPeriod(billDate, cycle)
127
237
 
128
- const cycle = await closePrepaidPeriod(em, account, period, billRun, {
129
- mode: params.mode,
130
- statementEnabled,
131
- previousClosing,
132
- threshold,
133
- nearLimitBuffer,
134
- creditLimit,
135
- })
136
- previousClosing = cycle.closingBalance
137
- // Recurring charges move the balance too, so they can trip the same
138
- // low/exhausted/over_limit crossings as a usage debit (SPEC-002). These
139
- // are accumulated and emitted by the engine AFTER the run commits.
140
- statementEvents.push(...cycle.crossingEvents)
141
-
142
- // A negative closing balance is NORMAL for prepaid (the customer overspent;
143
- // it's cleared by the next top-up), so it is not flagged as a warning.
144
- if (cycle.statementId) {
145
- lastStatementId = cycle.statementId
146
- statementEvents.push({
147
- id: 'billing.statement.generated',
148
- payload: {
149
- statementId: cycle.statementId,
150
- billAccountId: account.id,
151
- tenantId: account.tenantId,
152
- organizationId: account.organizationId,
153
- billRunId: billRun.id,
154
- periodStart: period.periodStart.toISOString().slice(0, 10),
155
- periodEnd: period.periodEnd.toISOString().slice(0, 10),
156
- currencyCode: account.currencyCode,
157
- openingBalance: cycle.openingBalance,
158
- closingBalance: cycle.closingBalance,
159
- totalTopups: cycle.totalTopups,
160
- totalUsage: cycle.totalUsage,
161
- totalRecurring: cycle.totalRecurring,
162
- totalAdjustments: cycle.totalAdjustments,
238
+ // ── Recurring: service periods against the in-memory watermarks ──
239
+ const charges: ChargeLine[] = []
240
+ const watermarkUpdates = new Map<string, Date | null>()
241
+ for (const item of recurringItems) {
242
+ const unitPrice = (item.rateJson as { unit_price?: number } | null)?.unit_price
243
+ if (typeof unitPrice !== 'number') continue
244
+ const computed = computeRecurringLines(
245
+ {
246
+ id: item.id,
247
+ chargeTiming: item.chargeTiming ?? 'in_advance',
248
+ prorate: item.prorate ?? true,
249
+ billStartDate: item.billStartDate,
250
+ billEndDate: item.billEndDate ?? null,
251
+ billedToDate: watermarks.get(item.id) ?? null,
252
+ unitPrice,
253
+ isActive: item.isActive,
254
+ deletedAt: item.deletedAt ?? null,
163
255
  },
256
+ { billDate, cycle },
257
+ )
258
+ pushSkips(computed.skipped)
259
+ if (computed.newBilledToDate?.getTime() !== watermarks.get(item.id)?.getTime()) {
260
+ watermarkUpdates.set(item.id, computed.newBilledToDate)
261
+ }
262
+ const descriptors = buildRecurringLineDescriptors(item, computed.lines)
263
+ descriptors.forEach((descriptor, index) => {
264
+ charges.push(toCharge(descriptor, computed.lines[index]?.kind ?? 'charge', ctx.taxPoint))
164
265
  })
165
266
  }
166
267
 
268
+ // ── One-time: first bill date whose [D − cycle, D + cycle − 1] window
269
+ // overlaps (same rule as postpaid) ──
270
+ const oneTimeWindow: BillPeriod = {
271
+ periodStart: shiftCycles(billDate, cycle, -1),
272
+ periodEnd: addDays(nextBillDate, -1),
273
+ }
274
+ const oneTimesThisCycle = oneTimeItems.filter(
275
+ (it) => !billedOneTimes.has(it.id) && shouldIncludeItem(it as SelectableItem, oneTimeWindow).include,
276
+ )
277
+ for (const descriptor of buildInvoiceLinesFromItems(oneTimesThisCycle)) {
278
+ charges.push(toCharge(descriptor, 'charge', ctx.taxPoint))
279
+ }
280
+
281
+ const closed = await closePrepaidPeriod(em, account, billRun, { billDate, period, charges, previousClosing }, ctx)
282
+ previousClosing = closed.closingBalance
283
+ statementEvents.push(...closed.events)
284
+ if (closed.statementId) lastStatementId = closed.statementId
285
+ cycles.push(closed.record)
286
+
287
+ // Advance the in-memory state (every mode); persist in real mode only.
288
+ for (const [itemId, watermark] of watermarkUpdates) watermarks.set(itemId, watermark)
289
+ for (const item of oneTimesThisCycle) billedOneTimes.add(item.id)
167
290
  if (params.mode === 'real') {
168
- account.nextBillDate = advanceNextBillDate(
169
- account.nextBillDate,
170
- account.billCycle as BillCycle,
171
- )
291
+ const now = new Date()
292
+ for (const item of recurringItems) {
293
+ if (!watermarkUpdates.has(item.id)) continue
294
+ item.billedToDate = watermarkUpdates.get(item.id) ?? null
295
+ item.updatedAt = now
296
+ }
297
+ for (const item of oneTimesThisCycle) {
298
+ item.billedToDate = period.periodEnd
299
+ item.updatedAt = now
300
+ }
301
+ account.nextBillDate = nextBillDate
172
302
  account.lastBillDate = period.periodEnd
173
- account.updatedAt = new Date()
303
+ account.updatedAt = now
174
304
  await em.flush()
175
- } else {
176
- // test / dry: do not advance state; one iteration only.
177
- cyclesEmitted += 1
178
- break
179
305
  }
180
- cyclesEmitted += 1
306
+ billDate = nextBillDate
181
307
  }
182
308
 
309
+ const finalWarnings: PrepaidAccountWarnings = {
310
+ ...warnings,
311
+ ...(skippedItems.length ? { skipped_items: skippedItems } : {}),
312
+ }
183
313
  return {
184
- status: 'success',
185
- warnings: null,
314
+ status: warnings.catch_up_cap_reached ? 'success_with_warnings' : 'success',
315
+ warnings: Object.keys(finalWarnings).length ? finalWarnings : null,
186
316
  statementId: lastStatementId,
187
- cyclesEmitted,
317
+ cyclesEmitted: cycles.filter((c) => c.status === 'charged' || c.status === 'previewed').length,
318
+ cycles,
188
319
  statementEvents,
189
320
  }
190
321
  }
191
322
 
323
+ type ClosePeriodInput = {
324
+ billDate: Date
325
+ period: BillPeriod
326
+ charges: ChargeLine[]
327
+ previousClosing: string | null
328
+ }
329
+
192
330
  type ClosePeriodResult = {
331
+ record: PrepaidCycleRecord
332
+ /** The statement CREATED by this close (null when skipped or not written). */
193
333
  statementId: string | null
194
- openingBalance: string
195
- totalTopups: string
196
- totalUsage: string
197
- totalRecurring: string
198
- totalAdjustments: string
199
334
  closingBalance: string
200
- crossingEvents: DomainEvent[]
335
+ events: DomainEvent[]
201
336
  }
202
337
 
203
338
  type DebitBreakdownEntry = {
204
339
  billing_item_id: string
205
340
  billing_type: string
206
- uom_code?: string
341
+ description: string
207
342
  quantity: string
343
+ /** What left the balance (kept as `amount` for older statement readers). */
208
344
  amount: string
345
+ net: string
346
+ tax: string
347
+ tax_rate: string
348
+ service_period_start: string | null
349
+ service_period_end: string | null
350
+ proration: unknown
209
351
  }
210
352
 
211
353
  async function closePrepaidPeriod(
212
354
  em: EntityManager,
213
355
  account: BillingAccount,
214
- period: BillPeriod,
215
356
  billRun: BillingRun,
216
- opts: {
217
- mode: PrepaidCloseMode
218
- statementEnabled: boolean
219
- previousClosing: string | null
220
- threshold: string
221
- nearLimitBuffer: string
222
- creditLimit: string
223
- },
357
+ input: ClosePeriodInput,
358
+ ctx: CycleContext,
224
359
  ): Promise<ClosePeriodResult> {
225
- const periodEndISO = period.periodEnd.toISOString().slice(0, 10)
360
+ const { period, charges } = input
361
+ const periodStartIso = toIsoDate(period.periodStart)
362
+ const periodEndIso = toIsoDate(period.periodEnd)
226
363
 
227
- // Recurring + one_time items eligible for this period (usage excluded by the
228
- // selector — it was rated in real time).
229
- const items = await em.find(BillingItem, {
230
- tenantId: account.tenantId,
231
- organizationId: account.organizationId,
232
- billAccountId: account.id,
233
- deletedAt: null,
234
- } as never)
235
- const { included } = selectItemsForPeriod(items as unknown as SelectableItem[], period)
236
- const billableItems = included as unknown as BillingItem[]
237
- const lines = buildInvoiceLinesFromItems(billableItems)
238
-
239
- // Window bounds for categorized sums (topups / usage already in the ledger).
240
- const windowStart = period.periodStart
241
- const windowEnd = addDays(period.periodEnd, 1) // exclusive upper bound
242
-
243
- // Opening chains from the prior statement's closing — NOT a created_at sum.
244
- // Recurring debits are stamped at run time (after period_end), so a
245
- // `SUM(amount WHERE created_at < period_start)` would drop the prior period's
246
- // recurring charge and break the chain. Resolution order:
247
- // 1. previous cycle's closing (catch-up, in this run),
248
- // 2. the prior persisted statement's closing (month-over-month),
249
- // 3. the ledger balance before the first-ever statement window.
250
- const openingBalance = await resolveOpeningBalance(em, account, period, opts.previousClosing)
364
+ // Statement anti-duplicate (real only — test / dry never write statements).
365
+ // Date-only parameters go as ISO strings (P-D5).
366
+ const existingStatement =
367
+ ctx.mode === 'real'
368
+ ? await em.findOne(BillingStatement, {
369
+ tenantId: account.tenantId,
370
+ billAccountId: account.id,
371
+ periodStart: periodStartIso,
372
+ periodEnd: periodEndIso,
373
+ } as never)
374
+ : null
375
+
376
+ // Ledger window of this statement: (previous bill date, this bill date] —
377
+ // i.e. from the day after the previous bill date up to and including the
378
+ // bill date D itself. Charges are taken AT D, so money that arrives on D
379
+ // (a top-up made on the bill date) belongs to the same statement as the
380
+ // charges — never an opening balance that pretends the account was short.
381
+ // Consecutive windows stay contiguous: [D_prev + 1, D + 1).
382
+ const windowStart = addDays(period.periodStart, 1)
383
+ const windowEnd = addDays(input.billDate, 1) // exclusive upper bound
384
+ const openingBalance = await resolveOpeningBalance(em, account, windowStart, period, input.previousClosing)
251
385
  const totalTopups = await sumByTypeInWindow(em, account, 'topup', windowStart, windowEnd)
252
- const totalUsage = absMoney(
253
- await sumByTypeInWindow(em, account, 'usage', windowStart, windowEnd),
254
- )
255
- // Manual adjustments / reversals that landed in the window — SIGNED (a credit
256
- // is +, a debit −). Kept as a first-class term so the statement is
257
- // self-consistent and the opening→closing chain never drifts.
386
+ const totalUsage = absMoney(await sumByTypeInWindow(em, account, 'usage', windowStart, windowEnd))
387
+ // Manual adjustments / reversals in the window — SIGNED. A first-class term
388
+ // so the statement stays self-consistent and the chain never drifts.
258
389
  const totalAdjustments = addMoney(
259
390
  await sumByTypeInWindow(em, account, 'adjustment', windowStart, windowEnd),
260
391
  await sumByTypeInWindow(em, account, 'reversal', windowStart, windowEnd),
261
392
  )
262
393
 
263
394
  let totalRecurring = '0.0000'
395
+ let totalNet = '0.0000'
264
396
  const breakdown: DebitBreakdownEntry[] = []
265
397
  const chargeTransactionIds: string[] = []
266
- // The balance just before / after the recurring batch — for crossing events.
267
- let recurringBalanceBefore: string | null = null
268
- let recurringBalanceAfter: string | null = null
398
+ let balanceBefore: string | null = null
399
+ let balanceAfter: string | null = null
269
400
 
270
- if (opts.mode === 'real') {
271
- const now = new Date()
272
- for (const line of lines) {
273
- const movement = await applyBalanceMovement(em, {
274
- organizationId: account.organizationId,
275
- tenantId: account.tenantId,
276
- billAccountId: account.id,
277
- currencyCode: account.currencyCode,
278
- type: line.billingType === 'one_time' ? 'one_time' : 'recurring',
279
- amount: fromUnits(-toUnits(line.totalNetAmount)),
280
- description: line.description,
281
- billingItemId: line.billingItemId,
282
- sourceRef: `recurring-${line.billingItemId}-${periodEndISO}`,
283
- metadata: {
284
- bill_period_start: period.periodStart.toISOString().slice(0, 10),
285
- bill_period_end: periodEndISO,
286
- bill_run_id: billRun.id,
287
- },
288
- })
289
- if (recurringBalanceBefore === null) recurringBalanceBefore = movement.balanceBefore
290
- recurringBalanceAfter = movement.balanceAfter
291
- totalRecurring = addMoney(totalRecurring, line.totalNetAmount)
292
- if (!movement.deduplicated) chargeTransactionIds.push(movement.transaction.id)
293
- breakdown.push({
294
- billing_item_id: line.billingItemId,
295
- billing_type: line.billingType,
296
- quantity: line.quantity,
297
- amount: line.totalNetAmount,
298
- })
299
- const item = billableItems.find((it) => it.id === line.billingItemId)
300
- if (item) {
301
- item.billedToDate = period.periodEnd
302
- item.updatedAt = now
303
- }
304
- }
305
- await em.flush()
306
- } else {
307
- // test / dry: compute the would-be charges, persist nothing.
308
- for (const line of lines) {
309
- totalRecurring = addMoney(totalRecurring, line.totalNetAmount)
310
- breakdown.push({
311
- billing_item_id: line.billingItemId,
312
- billing_type: line.billingType,
313
- quantity: line.quantity,
314
- amount: line.totalNetAmount,
315
- })
401
+ for (const charge of charges) {
402
+ const { descriptor, amounts } = charge
403
+ const debit = money4(amounts.debit)
404
+ totalRecurring = addMoney(totalRecurring, debit)
405
+ totalNet = addMoney(totalNet, money4(amounts.net))
406
+ const metadata = {
407
+ bill_date: toIsoDate(input.billDate),
408
+ bill_period_start: periodStartIso,
409
+ bill_period_end: periodEndIso,
410
+ bill_run_id: billRun.id,
411
+ service_period_start: descriptor.servicePeriodStart ? toIsoDate(descriptor.servicePeriodStart) : null,
412
+ service_period_end: descriptor.servicePeriodEnd ? toIsoDate(descriptor.servicePeriodEnd) : null,
413
+ net: money4(amounts.net),
414
+ tax: money4(amounts.tax),
415
+ tax_rate: String(amounts.taxRate),
416
+ tax_point: ctx.taxPoint,
417
+ ...(descriptor.metadataExtra ?? {}),
316
418
  }
419
+ breakdown.push({
420
+ billing_item_id: descriptor.billingItemId,
421
+ billing_type: descriptor.billingType,
422
+ description: descriptor.description,
423
+ quantity: descriptor.quantity,
424
+ amount: debit,
425
+ net: metadata.net,
426
+ tax: metadata.tax,
427
+ tax_rate: metadata.tax_rate,
428
+ service_period_start: metadata.service_period_start,
429
+ service_period_end: metadata.service_period_end,
430
+ proration: (descriptor.metadataExtra as { proration?: unknown } | undefined)?.proration ?? null,
431
+ })
432
+ if (ctx.mode !== 'real') continue
433
+ const movement = await applyBalanceMovement(em, {
434
+ organizationId: account.organizationId,
435
+ tenantId: account.tenantId,
436
+ billAccountId: account.id,
437
+ currencyCode: account.currencyCode,
438
+ // Credits back (an item ended inside a charged period) stay typed as the
439
+ // charge they reverse: adjustments are summed by date window on
440
+ // statements and would be counted twice.
441
+ type: descriptor.billingType === 'one_time' ? 'one_time' : 'recurring',
442
+ amount: fromUnits(-toUnits(debit)),
443
+ description: descriptor.description,
444
+ billingItemId: descriptor.billingItemId,
445
+ sourceRef: charge.sourceRef,
446
+ metadata,
447
+ })
448
+ if (balanceBefore === null) balanceBefore = movement.balanceBefore
449
+ balanceAfter = movement.balanceAfter
450
+ if (!movement.deduplicated) chargeTransactionIds.push(movement.transaction.id)
317
451
  }
452
+ if (ctx.mode === 'real' && charges.length > 0) await em.flush()
318
453
 
319
- // closing = opening + topups + adjustments − usage − recurring.
454
+ // closing = opening + top-ups + adjustments − usage − charges.
320
455
  const closingBalance = subMoney(
321
456
  subMoney(addMoney(addMoney(openingBalance, totalTopups), totalAdjustments), totalUsage),
322
457
  totalRecurring,
323
458
  )
459
+ const vatSummary: VatSummaryRow[] = buildVatSummary(
460
+ charges.map((c) => c.amounts),
461
+ ctx.taxPoint,
462
+ )
324
463
 
325
- // The recurring batch can push the balance low / exhausted / over-limit —
326
- // emit those crossings (real mode only; dry/test never move the balance).
327
- const crossingEvents =
328
- recurringBalanceBefore !== null && recurringBalanceAfter !== null
464
+ const events: DomainEvent[] =
465
+ balanceBefore !== null && balanceAfter !== null
329
466
  ? buildBalanceCrossingEvents({
330
467
  scope: {
331
468
  billAccountId: account.id,
@@ -333,16 +470,16 @@ async function closePrepaidPeriod(
333
470
  organizationId: account.organizationId,
334
471
  currencyCode: account.currencyCode,
335
472
  },
336
- balanceBefore: recurringBalanceBefore,
337
- balanceAfter: recurringBalanceAfter,
338
- threshold: opts.threshold,
339
- creditLimit: opts.creditLimit,
340
- nearLimitBuffer: opts.nearLimitBuffer,
473
+ balanceBefore,
474
+ balanceAfter,
475
+ threshold: ctx.threshold,
476
+ creditLimit: ctx.creditLimit,
477
+ nearLimitBuffer: ctx.nearLimitBuffer,
341
478
  })
342
479
  : []
343
480
 
344
- let statementId: string | null = null
345
- if (opts.mode === 'real' && opts.statementEnabled) {
481
+ let createdStatementId: string | null = null
482
+ if (ctx.mode === 'real' && ctx.statementEnabled && !existingStatement) {
346
483
  const now = new Date()
347
484
  const statement = em.create(BillingStatement, {
348
485
  organizationId: account.organizationId,
@@ -358,45 +495,80 @@ async function closePrepaidPeriod(
358
495
  totalRecurring,
359
496
  totalAdjustments,
360
497
  closingBalance,
361
- debitBreakdown: { items: breakdown },
498
+ debitBreakdown: { items: breakdown, bill_date: toIsoDate(input.billDate), tax_point: ctx.taxPoint },
499
+ vatSummary,
362
500
  status: 'generated',
363
501
  createdAt: now,
364
502
  updatedAt: now,
365
503
  })
366
504
  em.persist(statement)
367
505
  await em.flush()
368
- statementId = statement.id
369
-
370
- // Backfill statement_id on the charge transactions for attribution.
506
+ createdStatementId = statement.id
371
507
  if (chargeTransactionIds.length > 0) {
372
508
  await em.nativeUpdate(
373
509
  BillingAccountTransaction,
374
510
  { id: { $in: chargeTransactionIds } } as never,
375
- { statementId } as never,
511
+ { statementId: createdStatementId } as never,
376
512
  )
377
513
  }
514
+ events.push({
515
+ id: 'billing.statement.generated',
516
+ payload: {
517
+ statementId: createdStatementId,
518
+ billAccountId: account.id,
519
+ tenantId: account.tenantId,
520
+ organizationId: account.organizationId,
521
+ billRunId: billRun.id,
522
+ periodStart: periodStartIso,
523
+ periodEnd: periodEndIso,
524
+ currencyCode: account.currencyCode,
525
+ openingBalance,
526
+ closingBalance,
527
+ totalTopups,
528
+ totalUsage,
529
+ totalRecurring,
530
+ totalAdjustments,
531
+ },
532
+ })
378
533
  }
379
534
 
535
+ let status: PrepaidCycleStatus
536
+ if (ctx.mode !== 'real') status = charges.length > 0 ? 'previewed' : 'empty'
537
+ else if (existingStatement && chargeTransactionIds.length === 0) status = 'skipped_existing'
538
+ else status = chargeTransactionIds.length > 0 ? 'charged' : 'empty'
539
+
380
540
  return {
381
- statementId,
382
- openingBalance,
383
- totalTopups,
384
- totalUsage,
385
- totalRecurring,
386
- totalAdjustments,
541
+ record: {
542
+ bill_date: toIsoDate(input.billDate),
543
+ period_start: periodStartIso,
544
+ period_end: periodEndIso,
545
+ status,
546
+ line_count: charges.length,
547
+ total_net: totalNet,
548
+ total_debit: totalRecurring,
549
+ invoice_id: null,
550
+ statement_id: createdStatementId ?? existingStatement?.id ?? null,
551
+ },
552
+ statementId: createdStatementId,
387
553
  closingBalance,
388
- crossingEvents,
554
+ events,
389
555
  }
390
556
  }
391
557
 
392
558
  /**
393
559
  * Resolve a period's opening balance by chaining from the prior statement's
394
560
  * closing (the spec's "chains cleanly"), with a ledger fallback for the very
395
- * first statement. See the call site for why a created_at sum alone is wrong.
561
+ * first statement. Charges are stamped at run time (after the window), so a
562
+ * `SUM(amount WHERE created_at < period_start)` would drop the prior
563
+ * period's charges and break the chain. Resolution order:
564
+ * 1. previous cycle's closing (catch-up, in this run),
565
+ * 2. the prior persisted statement's closing (month-over-month),
566
+ * 3. the ledger balance before the first-ever statement window.
396
567
  */
397
568
  async function resolveOpeningBalance(
398
569
  em: EntityManager,
399
570
  account: BillingAccount,
571
+ windowStart: Date,
400
572
  period: BillPeriod,
401
573
  previousClosing: string | null,
402
574
  ): Promise<string> {
@@ -407,22 +579,18 @@ async function resolveOpeningBalance(
407
579
  tenantId: account.tenantId,
408
580
  organizationId: account.organizationId,
409
581
  billAccountId: account.id,
410
- periodEnd: { $lt: period.periodStart },
582
+ periodEnd: { $lt: toIsoDate(period.periodStart) },
411
583
  } as never,
412
584
  { orderBy: { periodEnd: 'DESC' } },
413
585
  )
414
586
  if (prior) return fromUnits(toUnits(prior.closingBalance))
415
- // First-ever statement: the ledger balance just before the period window
416
- // (only top-ups/usage exist before the first close, so created_at is safe).
417
- return sumAmountsBefore(em, account, period.periodStart)
587
+ // First-ever statement: the ledger balance just before its window (only
588
+ // top-ups / usage exist before the first close, so created_at is safe).
589
+ return sumAmountsBefore(em, account, windowStart)
418
590
  }
419
591
 
420
592
  /** Balance at `before` = SUM(amount) of transactions created strictly before it. */
421
- async function sumAmountsBefore(
422
- em: EntityManager,
423
- account: BillingAccount,
424
- before: Date,
425
- ): Promise<string> {
593
+ async function sumAmountsBefore(em: EntityManager, account: BillingAccount, before: Date): Promise<string> {
426
594
  const rows = (await em.execute(
427
595
  `SELECT COALESCE(SUM(amount), 0) AS total
428
596
  FROM billing_account_transactions