@dainamite/billing 0.19.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 (614) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/README.md +14 -0
  3. package/dist/modules/billing/acl.js +13 -1
  4. package/dist/modules/billing/acl.js.map +2 -2
  5. package/dist/modules/billing/api/accounts/[id]/summary/route.js +52 -0
  6. package/dist/modules/billing/api/accounts/[id]/summary/route.js.map +7 -0
  7. package/dist/modules/billing/api/accounts/route.js +46 -4
  8. package/dist/modules/billing/api/accounts/route.js.map +2 -2
  9. package/dist/modules/billing/api/export/account/[id]/route.js +16 -12
  10. package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
  11. package/dist/modules/billing/api/invoices/[id]/memos/route.js +54 -0
  12. package/dist/modules/billing/api/invoices/[id]/memos/route.js.map +7 -0
  13. package/dist/modules/billing/api/invoices/[id]/pdf/route.js +69 -0
  14. package/dist/modules/billing/api/invoices/[id]/pdf/route.js.map +7 -0
  15. package/dist/modules/billing/api/invoices/[id]/route.js +44 -24
  16. package/dist/modules/billing/api/invoices/[id]/route.js.map +2 -2
  17. package/dist/modules/billing/api/invoices/[id]/send/route.js +107 -0
  18. package/dist/modules/billing/api/invoices/[id]/send/route.js.map +7 -0
  19. package/dist/modules/billing/api/invoices/add-line/route.js +3 -3
  20. package/dist/modules/billing/api/invoices/add-line/route.js.map +2 -2
  21. package/dist/modules/billing/api/invoices/delete-draft/route.js +33 -0
  22. package/dist/modules/billing/api/invoices/delete-draft/route.js.map +7 -0
  23. package/dist/modules/billing/api/invoices/edit-line/route.js +3 -3
  24. package/dist/modules/billing/api/invoices/edit-line/route.js.map +2 -2
  25. package/dist/modules/billing/api/invoices/post/route.js +2 -2
  26. package/dist/modules/billing/api/invoices/post/route.js.map +2 -2
  27. package/dist/modules/billing/api/invoices/remove-line/route.js +3 -3
  28. package/dist/modules/billing/api/invoices/remove-line/route.js.map +2 -2
  29. package/dist/modules/billing/api/invoices/route.js +45 -46
  30. package/dist/modules/billing/api/invoices/route.js.map +2 -2
  31. package/dist/modules/billing/api/items/bulk/route.js +1 -1
  32. package/dist/modules/billing/api/items/bulk/route.js.map +2 -2
  33. package/dist/modules/billing/api/items/change-price/route.js +44 -0
  34. package/dist/modules/billing/api/items/change-price/route.js.map +7 -0
  35. package/dist/modules/billing/api/items/route.js +19 -7
  36. package/dist/modules/billing/api/items/route.js.map +2 -2
  37. package/dist/modules/billing/api/payment-imports/[id]/route.js +49 -0
  38. package/dist/modules/billing/api/payment-imports/[id]/route.js.map +7 -0
  39. package/dist/modules/billing/api/payment-imports/route.js +57 -0
  40. package/dist/modules/billing/api/payment-imports/route.js.map +7 -0
  41. package/dist/modules/billing/api/payments/[id]/route.js +60 -0
  42. package/dist/modules/billing/api/payments/[id]/route.js.map +7 -0
  43. package/dist/modules/billing/api/payments/allocate/route.js +35 -0
  44. package/dist/modules/billing/api/payments/allocate/route.js.map +7 -0
  45. package/dist/modules/billing/api/payments/assign/route.js +36 -0
  46. package/dist/modules/billing/api/payments/assign/route.js.map +7 -0
  47. package/dist/modules/billing/api/payments/convert-to-topup/route.js +35 -0
  48. package/dist/modules/billing/api/payments/convert-to-topup/route.js.map +7 -0
  49. package/dist/modules/billing/api/payments/ignore/route.js +35 -0
  50. package/dist/modules/billing/api/payments/ignore/route.js.map +7 -0
  51. package/dist/modules/billing/api/payments/import/route.js +63 -0
  52. package/dist/modules/billing/api/payments/import/route.js.map +7 -0
  53. package/dist/modules/billing/api/payments/restore/route.js +35 -0
  54. package/dist/modules/billing/api/payments/restore/route.js.map +7 -0
  55. package/dist/modules/billing/api/payments/route.js +114 -0
  56. package/dist/modules/billing/api/payments/route.js.map +7 -0
  57. package/dist/modules/billing/api/payments/unallocate/route.js +35 -0
  58. package/dist/modules/billing/api/payments/unallocate/route.js.map +7 -0
  59. package/dist/modules/billing/api/payments/unassign/route.js +35 -0
  60. package/dist/modules/billing/api/payments/unassign/route.js.map +7 -0
  61. package/dist/modules/billing/api/run-outcomes/route.js +44 -7
  62. package/dist/modules/billing/api/run-outcomes/route.js.map +2 -2
  63. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js +61 -0
  64. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js.map +7 -0
  65. package/dist/modules/billing/api/runs/[id]/route.js +53 -0
  66. package/dist/modules/billing/api/runs/[id]/route.js.map +7 -0
  67. package/dist/modules/billing/api/runs/retry-failed/route.js +3 -3
  68. package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
  69. package/dist/modules/billing/api/runs/route.js +11 -5
  70. package/dist/modules/billing/api/runs/route.js.map +2 -2
  71. package/dist/modules/billing/api/settings/number-preview/route.js +42 -0
  72. package/dist/modules/billing/api/settings/number-preview/route.js.map +7 -0
  73. package/dist/modules/billing/api/settings/route.js +92 -0
  74. package/dist/modules/billing/api/settings/route.js.map +7 -0
  75. package/dist/modules/billing/api/statements/route.js +3 -0
  76. package/dist/modules/billing/api/statements/route.js.map +2 -2
  77. package/dist/modules/billing/api/test-invoices/wipe/route.js +2 -2
  78. package/dist/modules/billing/api/test-invoices/wipe/route.js.map +2 -2
  79. package/dist/modules/billing/backend/billing/accounts/[id]/page.js +121 -232
  80. package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
  81. package/dist/modules/billing/backend/billing/accounts/create/page.js +6 -0
  82. package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
  83. package/dist/modules/billing/backend/billing/accounts/page.js +63 -4
  84. package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
  85. package/dist/modules/billing/backend/billing/invoices/[id]/page.js +297 -65
  86. package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +3 -3
  87. package/dist/modules/billing/backend/billing/invoices/page.js +6 -172
  88. package/dist/modules/billing/backend/billing/invoices/page.js.map +2 -2
  89. package/dist/modules/billing/backend/billing/items/[id]/page.js +51 -7
  90. package/dist/modules/billing/backend/billing/items/[id]/page.js.map +2 -2
  91. package/dist/modules/billing/backend/billing/items/create/page.js +9 -6
  92. package/dist/modules/billing/backend/billing/items/create/page.js.map +2 -2
  93. package/dist/modules/billing/backend/billing/items/page.js +8 -2
  94. package/dist/modules/billing/backend/billing/items/page.js.map +2 -2
  95. package/dist/modules/billing/backend/billing/payments/[id]/page.js +297 -0
  96. package/dist/modules/billing/backend/billing/payments/[id]/page.js.map +7 -0
  97. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js +21 -0
  98. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js.map +7 -0
  99. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js +74 -0
  100. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js.map +7 -0
  101. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js +21 -0
  102. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js.map +7 -0
  103. package/dist/modules/billing/backend/billing/payments/page.js +58 -0
  104. package/dist/modules/billing/backend/billing/payments/page.js.map +7 -0
  105. package/dist/modules/billing/backend/billing/payments/page.meta.js +18 -0
  106. package/dist/modules/billing/backend/billing/payments/page.meta.js.map +7 -0
  107. package/dist/modules/billing/backend/billing/runs/[id]/page.js +49 -14
  108. package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +2 -2
  109. package/dist/modules/billing/backend/billing/runs/page.js +58 -7
  110. package/dist/modules/billing/backend/billing/runs/page.js.map +2 -2
  111. package/dist/modules/billing/backend/billing/statements/[id]/page.js +61 -6
  112. package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +2 -2
  113. package/dist/modules/billing/backend/billing/statements/page.js +1 -1
  114. package/dist/modules/billing/backend/billing/statements/page.js.map +2 -2
  115. package/dist/modules/billing/backend/billing/topups/page.js +1 -1
  116. package/dist/modules/billing/backend/billing/topups/page.js.map +2 -2
  117. package/dist/modules/billing/backend/billing/transactions/page.js +1 -1
  118. package/dist/modules/billing/backend/billing/transactions/page.js.map +2 -2
  119. package/dist/modules/billing/backend/billing/usage/create/page.js +3 -2
  120. package/dist/modules/billing/backend/billing/usage/create/page.js.map +2 -2
  121. package/dist/modules/billing/backend/config/billing/page.js +472 -0
  122. package/dist/modules/billing/backend/config/billing/page.js.map +7 -0
  123. package/dist/modules/billing/backend/config/billing/page.meta.js +19 -0
  124. package/dist/modules/billing/backend/config/billing/page.meta.js.map +7 -0
  125. package/dist/modules/billing/ce.js.map +2 -2
  126. package/dist/modules/billing/cli.js +22 -1
  127. package/dist/modules/billing/cli.js.map +2 -2
  128. package/dist/modules/billing/commands/accounts.js +6 -0
  129. package/dist/modules/billing/commands/accounts.js.map +2 -2
  130. package/dist/modules/billing/commands/index.js +3 -0
  131. package/dist/modules/billing/commands/index.js.map +2 -2
  132. package/dist/modules/billing/commands/invoices.js +272 -170
  133. package/dist/modules/billing/commands/invoices.js.map +2 -2
  134. package/dist/modules/billing/commands/items.js +108 -0
  135. package/dist/modules/billing/commands/items.js.map +2 -2
  136. package/dist/modules/billing/commands/memos.js +179 -0
  137. package/dist/modules/billing/commands/memos.js.map +7 -0
  138. package/dist/modules/billing/commands/payments.js +379 -0
  139. package/dist/modules/billing/commands/payments.js.map +7 -0
  140. package/dist/modules/billing/commands/runs.js +154 -7
  141. package/dist/modules/billing/commands/runs.js.map +2 -2
  142. package/dist/modules/billing/commands/settings.js +59 -0
  143. package/dist/modules/billing/commands/settings.js.map +7 -0
  144. package/dist/modules/billing/commands/topups.js +9 -2
  145. package/dist/modules/billing/commands/topups.js.map +2 -2
  146. package/dist/modules/billing/commands/transactions.js +2 -2
  147. package/dist/modules/billing/commands/transactions.js.map +2 -2
  148. package/dist/modules/billing/components/AccountForm.js +149 -42
  149. package/dist/modules/billing/components/AccountForm.js.map +2 -2
  150. package/dist/modules/billing/components/AccountInvoicesSection.js +64 -0
  151. package/dist/modules/billing/components/AccountInvoicesSection.js.map +7 -0
  152. package/dist/modules/billing/components/AccountPaymentsSection.js +43 -0
  153. package/dist/modules/billing/components/AccountPaymentsSection.js.map +7 -0
  154. package/dist/modules/billing/components/AccountPicker.js +56 -0
  155. package/dist/modules/billing/components/AccountPicker.js.map +7 -0
  156. package/dist/modules/billing/components/AllocatePaymentDialog.js +134 -0
  157. package/dist/modules/billing/components/AllocatePaymentDialog.js.map +7 -0
  158. package/dist/modules/billing/components/BillingDataTable.js +21 -0
  159. package/dist/modules/billing/components/BillingDataTable.js.map +7 -0
  160. package/dist/modules/billing/components/BillingSettingsLink.js +19 -0
  161. package/dist/modules/billing/components/BillingSettingsLink.js.map +7 -0
  162. package/dist/modules/billing/components/ChangeItemPriceDialog.js +158 -0
  163. package/dist/modules/billing/components/ChangeItemPriceDialog.js.map +7 -0
  164. package/dist/modules/billing/components/CreateMemoDialog.js +200 -0
  165. package/dist/modules/billing/components/CreateMemoDialog.js.map +7 -0
  166. package/dist/modules/billing/components/CustomerLabel.js +33 -0
  167. package/dist/modules/billing/components/CustomerLabel.js.map +7 -0
  168. package/dist/modules/billing/components/InvoicesTable.js +270 -0
  169. package/dist/modules/billing/components/InvoicesTable.js.map +7 -0
  170. package/dist/modules/billing/components/ItemForm.js +176 -44
  171. package/dist/modules/billing/components/ItemForm.js.map +2 -2
  172. package/dist/modules/billing/components/ItemPriceField.js +46 -0
  173. package/dist/modules/billing/components/ItemPriceField.js.map +7 -0
  174. package/dist/modules/billing/components/LineFormDialog.js +25 -2
  175. package/dist/modules/billing/components/LineFormDialog.js.map +2 -2
  176. package/dist/modules/billing/components/ManualPaymentDialog.js +122 -0
  177. package/dist/modules/billing/components/ManualPaymentDialog.js.map +7 -0
  178. package/dist/modules/billing/components/MoneyInput.js +14 -0
  179. package/dist/modules/billing/components/MoneyInput.js.map +7 -0
  180. package/dist/modules/billing/components/PaymentUploadDialog.js +115 -0
  181. package/dist/modules/billing/components/PaymentUploadDialog.js.map +7 -0
  182. package/dist/modules/billing/components/PaymentsTable.js +171 -0
  183. package/dist/modules/billing/components/PaymentsTable.js.map +7 -0
  184. package/dist/modules/billing/components/PostRunDraftsButton.js +105 -0
  185. package/dist/modules/billing/components/PostRunDraftsButton.js.map +7 -0
  186. package/dist/modules/billing/components/PrepaidPanel.js +14 -4
  187. package/dist/modules/billing/components/PrepaidPanel.js.map +2 -2
  188. package/dist/modules/billing/components/RunCyclesSection.js +209 -0
  189. package/dist/modules/billing/components/RunCyclesSection.js.map +7 -0
  190. package/dist/modules/billing/components/TestRunActions.js +127 -0
  191. package/dist/modules/billing/components/TestRunActions.js.map +7 -0
  192. package/dist/modules/billing/components/account/AccountItemsTab.js +157 -0
  193. package/dist/modules/billing/components/account/AccountItemsTab.js.map +7 -0
  194. package/dist/modules/billing/components/account/AccountRunBillingDialog.js +122 -0
  195. package/dist/modules/billing/components/account/AccountRunBillingDialog.js.map +7 -0
  196. package/dist/modules/billing/components/account/AccountRunsTab.js +152 -0
  197. package/dist/modules/billing/components/account/AccountRunsTab.js.map +7 -0
  198. package/dist/modules/billing/components/account/AccountStatementsTab.js +98 -0
  199. package/dist/modules/billing/components/account/AccountStatementsTab.js.map +7 -0
  200. package/dist/modules/billing/components/account/AccountSummaryCards.js +125 -0
  201. package/dist/modules/billing/components/account/AccountSummaryCards.js.map +7 -0
  202. package/dist/modules/billing/components/account/AccountUsageTab.js +91 -0
  203. package/dist/modules/billing/components/account/AccountUsageTab.js.map +7 -0
  204. package/dist/modules/billing/components/account/AccountView.js +73 -0
  205. package/dist/modules/billing/components/account/AccountView.js.map +7 -0
  206. package/dist/modules/billing/components/account/accountRow.js +39 -0
  207. package/dist/modules/billing/components/account/accountRow.js.map +7 -0
  208. package/dist/modules/billing/components/customerNames.js +42 -0
  209. package/dist/modules/billing/components/customerNames.js.map +7 -0
  210. package/dist/modules/billing/data/entities.js +615 -59
  211. package/dist/modules/billing/data/entities.js.map +2 -2
  212. package/dist/modules/billing/data/entityIds.js +10 -1
  213. package/dist/modules/billing/data/entityIds.js.map +2 -2
  214. package/dist/modules/billing/data/validators.js +269 -15
  215. package/dist/modules/billing/data/validators.js.map +2 -2
  216. package/dist/modules/billing/events.js +70 -0
  217. package/dist/modules/billing/events.js.map +2 -2
  218. package/dist/modules/billing/i18n/en.json +457 -21
  219. package/dist/modules/billing/i18n/pl.json +457 -21
  220. package/dist/modules/billing/index.js +6 -2
  221. package/dist/modules/billing/index.js.map +2 -2
  222. package/dist/modules/billing/lib/accountFinancials.js +58 -0
  223. package/dist/modules/billing/lib/accountFinancials.js.map +7 -0
  224. package/dist/modules/billing/lib/accountLock.js +14 -0
  225. package/dist/modules/billing/lib/accountLock.js.map +7 -0
  226. package/dist/modules/billing/lib/accountStatus.js +9 -7
  227. package/dist/modules/billing/lib/accountStatus.js.map +2 -2
  228. package/dist/modules/billing/lib/accountSummary.js +106 -0
  229. package/dist/modules/billing/lib/accountSummary.js.map +7 -0
  230. package/dist/modules/billing/lib/allocation.js +356 -0
  231. package/dist/modules/billing/lib/allocation.js.map +7 -0
  232. package/dist/modules/billing/lib/allocationEvents.js +22 -0
  233. package/dist/modules/billing/lib/allocationEvents.js.map +7 -0
  234. package/dist/modules/billing/lib/billPeriod.js +15 -1
  235. package/dist/modules/billing/lib/billPeriod.js.map +2 -2
  236. package/dist/modules/billing/lib/billRunEngine.js +241 -106
  237. package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
  238. package/dist/modules/billing/lib/billRunLabel.js.map +1 -1
  239. package/dist/modules/billing/lib/billingSettings.js +116 -0
  240. package/dist/modules/billing/lib/billingSettings.js.map +7 -0
  241. package/dist/modules/billing/lib/commandRoute.js +81 -0
  242. package/dist/modules/billing/lib/commandRoute.js.map +7 -0
  243. package/dist/modules/billing/lib/customerLookup.js +97 -0
  244. package/dist/modules/billing/lib/customerLookup.js.map +7 -0
  245. package/dist/modules/billing/lib/dateOnly.js +13 -0
  246. package/dist/modules/billing/lib/dateOnly.js.map +7 -0
  247. package/dist/modules/billing/lib/documentParties.js +16 -0
  248. package/dist/modules/billing/lib/documentParties.js.map +7 -0
  249. package/dist/modules/billing/lib/documentTypes.js +27 -0
  250. package/dist/modules/billing/lib/documentTypes.js.map +7 -0
  251. package/dist/modules/billing/lib/dueDate.js +18 -0
  252. package/dist/modules/billing/lib/dueDate.js.map +7 -0
  253. package/dist/modules/billing/lib/iban.js +29 -0
  254. package/dist/modules/billing/lib/iban.js.map +7 -0
  255. package/dist/modules/billing/lib/invoiceDelivery.js +131 -0
  256. package/dist/modules/billing/lib/invoiceDelivery.js.map +7 -0
  257. package/dist/modules/billing/lib/invoiceLanguages.js +16 -0
  258. package/dist/modules/billing/lib/invoiceLanguages.js.map +7 -0
  259. package/dist/modules/billing/lib/invoiceWriter.js +107 -99
  260. package/dist/modules/billing/lib/invoiceWriter.js.map +2 -2
  261. package/dist/modules/billing/lib/itemBillingState.js +27 -0
  262. package/dist/modules/billing/lib/itemBillingState.js.map +7 -0
  263. package/dist/modules/billing/lib/itemPriceChange.js +18 -0
  264. package/dist/modules/billing/lib/itemPriceChange.js.map +7 -0
  265. package/dist/modules/billing/lib/itemSelector.js.map +2 -2
  266. package/dist/modules/billing/lib/lineAmounts.js +32 -0
  267. package/dist/modules/billing/lib/lineAmounts.js.map +7 -0
  268. package/dist/modules/billing/lib/memoGuards.js +25 -0
  269. package/dist/modules/billing/lib/memoGuards.js.map +7 -0
  270. package/dist/modules/billing/lib/navigation.js +48 -0
  271. package/dist/modules/billing/lib/navigation.js.map +7 -0
  272. package/dist/modules/billing/lib/numbering.js +120 -0
  273. package/dist/modules/billing/lib/numbering.js.map +7 -0
  274. package/dist/modules/billing/lib/overdue.js +36 -0
  275. package/dist/modules/billing/lib/overdue.js.map +7 -0
  276. package/dist/modules/billing/lib/paymentCsv.js +125 -0
  277. package/dist/modules/billing/lib/paymentCsv.js.map +7 -0
  278. package/dist/modules/billing/lib/paymentGateway.js +15 -0
  279. package/dist/modules/billing/lib/paymentGateway.js.map +7 -0
  280. package/dist/modules/billing/lib/paymentImport.js +164 -0
  281. package/dist/modules/billing/lib/paymentImport.js.map +7 -0
  282. package/dist/modules/billing/lib/paymentMatcher.js +36 -0
  283. package/dist/modules/billing/lib/paymentMatcher.js.map +7 -0
  284. package/dist/modules/billing/lib/pdf/attachmentStore.js +46 -0
  285. package/dist/modules/billing/lib/pdf/attachmentStore.js.map +7 -0
  286. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js +7 -0
  287. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js.map +7 -0
  288. package/dist/modules/billing/lib/pdf/invoiceModel.js +194 -0
  289. package/dist/modules/billing/lib/pdf/invoiceModel.js.map +7 -0
  290. package/dist/modules/billing/lib/pdf/invoicePdf.js +165 -0
  291. package/dist/modules/billing/lib/pdf/invoicePdf.js.map +7 -0
  292. package/dist/modules/billing/lib/pdf/invoiceTemplate.js +274 -0
  293. package/dist/modules/billing/lib/pdf/invoiceTemplate.js.map +7 -0
  294. package/dist/modules/billing/lib/prepaidConfig.js +17 -25
  295. package/dist/modules/billing/lib/prepaidConfig.js.map +2 -2
  296. package/dist/modules/billing/lib/prepaidConsume.js +26 -7
  297. package/dist/modules/billing/lib/prepaidConsume.js.map +2 -2
  298. package/dist/modules/billing/lib/prepaidPeriodClose.js +277 -158
  299. package/dist/modules/billing/lib/prepaidPeriodClose.js.map +2 -2
  300. package/dist/modules/billing/lib/prepaidTaxPoint.js +37 -0
  301. package/dist/modules/billing/lib/prepaidTaxPoint.js.map +7 -0
  302. package/dist/modules/billing/lib/seeds/demo.js +51 -3
  303. package/dist/modules/billing/lib/seeds/demo.js.map +2 -2
  304. package/dist/modules/billing/lib/seeds/paymentsSample.js +93 -0
  305. package/dist/modules/billing/lib/seeds/paymentsSample.js.map +7 -0
  306. package/dist/modules/billing/lib/seeds/sellerSettings.js +41 -0
  307. package/dist/modules/billing/lib/seeds/sellerSettings.js.map +7 -0
  308. package/dist/modules/billing/lib/servicePeriods.js +121 -0
  309. package/dist/modules/billing/lib/servicePeriods.js.map +7 -0
  310. package/dist/modules/billing/lib/tenantLock.js.map +1 -1
  311. package/dist/modules/billing/lib/testRunCleanup.js +24 -0
  312. package/dist/modules/billing/lib/testRunCleanup.js.map +7 -0
  313. package/dist/modules/billing/lib/topupCapture.js +2 -2
  314. package/dist/modules/billing/lib/topupCapture.js.map +2 -2
  315. package/dist/modules/billing/lib/topupReceipt.js +58 -79
  316. package/dist/modules/billing/lib/topupReceipt.js.map +2 -2
  317. package/dist/modules/billing/lib/usageRater.js.map +1 -1
  318. package/dist/modules/billing/lib/usageRunner.js +14 -6
  319. package/dist/modules/billing/lib/usageRunner.js.map +2 -2
  320. package/dist/modules/billing/lib/vat.js +39 -0
  321. package/dist/modules/billing/lib/vat.js.map +7 -0
  322. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js +15 -0
  323. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js.map +7 -0
  324. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js +25 -0
  325. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js.map +7 -0
  326. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js +115 -0
  327. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js.map +7 -0
  328. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js +47 -0
  329. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js.map +7 -0
  330. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js +89 -0
  331. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js.map +7 -0
  332. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js +17 -0
  333. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js.map +7 -0
  334. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js +27 -0
  335. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js.map +7 -0
  336. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js +29 -0
  337. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js.map +7 -0
  338. package/dist/modules/billing/setup.js +7 -184
  339. package/dist/modules/billing/setup.js.map +2 -2
  340. package/dist/modules/billing/subscribers/credit-memo-auto-send.js +16 -0
  341. package/dist/modules/billing/subscribers/credit-memo-auto-send.js.map +7 -0
  342. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js +16 -0
  343. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js.map +7 -0
  344. package/dist/modules/billing/subscribers/debit-memo-auto-send.js +16 -0
  345. package/dist/modules/billing/subscribers/debit-memo-auto-send.js.map +7 -0
  346. package/dist/modules/billing/subscribers/invoice-apply-credit.js +34 -0
  347. package/dist/modules/billing/subscribers/invoice-apply-credit.js.map +7 -0
  348. package/dist/modules/billing/subscribers/invoice-auto-send.js +30 -0
  349. package/dist/modules/billing/subscribers/invoice-auto-send.js.map +7 -0
  350. package/dist/modules/billing/subscribers/payment-captured-to-paid.js +69 -33
  351. package/dist/modules/billing/subscribers/payment-captured-to-paid.js.map +2 -2
  352. package/dist/modules/billing/subscribers/prepaid-topup-captured.js +4 -6
  353. package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +2 -2
  354. package/dist/modules/billing/workers/reap-stale-bill-runs.js +2 -2
  355. package/dist/modules/billing/workers/reap-stale-bill-runs.js.map +2 -2
  356. package/generated/entities/billing_account/index.ts +26 -0
  357. package/generated/entities/billing_account_balance/index.ts +13 -0
  358. package/generated/entities/billing_account_transaction/index.ts +21 -0
  359. package/generated/entities/billing_account_usage/index.ts +18 -0
  360. package/generated/entities/billing_allocation/index.ts +18 -0
  361. package/generated/entities/billing_document_sequence/index.ts +11 -0
  362. package/generated/entities/billing_draft_invoice_edit/index.ts +14 -0
  363. package/generated/entities/billing_invoice/index.ts +38 -0
  364. package/generated/entities/billing_invoice_line/index.ts +24 -0
  365. package/generated/entities/billing_item/index.ts +26 -0
  366. package/generated/entities/billing_payment/index.ts +25 -0
  367. package/generated/entities/billing_payment_import/index.ts +14 -0
  368. package/generated/entities/billing_run/index.ts +20 -0
  369. package/generated/entities/billing_run_outcome/index.ts +18 -0
  370. package/generated/entities/billing_settings/index.ts +26 -0
  371. package/generated/entities/billing_statement/index.ts +22 -0
  372. package/generated/entities/billing_topup/index.ts +20 -0
  373. package/generated/entities.ids.generated.ts +35 -0
  374. package/package.json +15 -10
  375. package/src/modules/billing/__integration__/TC-BILL-004.spec.ts +170 -0
  376. package/src/modules/billing/__integration__/TC-BILL-005.spec.ts +134 -0
  377. package/src/modules/billing/__integration__/TC-BILL-006.spec.ts +101 -0
  378. package/src/modules/billing/__integration__/TC-BILL-007.spec.ts +140 -0
  379. package/src/modules/billing/__integration__/TC-BILL-008.spec.ts +108 -0
  380. package/src/modules/billing/__integration__/TC-BILL-009.spec.ts +113 -0
  381. package/src/modules/billing/__integration__/TC-BILL-010.spec.ts +118 -0
  382. package/src/modules/billing/__integration__/TC-BILL-011.spec.ts +142 -0
  383. package/src/modules/billing/__integration__/TC-BILL-012.spec.ts +115 -0
  384. package/src/modules/billing/__integration__/TC-BILL-013.spec.ts +118 -0
  385. package/src/modules/billing/__integration__/sellerSettings.ts +24 -0
  386. package/src/modules/billing/__tests__/commands/invoices.test.ts +437 -355
  387. package/src/modules/billing/__tests__/commands/itemsBulk.test.ts +33 -0
  388. package/src/modules/billing/__tests__/commands/memos.test.ts +205 -0
  389. package/src/modules/billing/__tests__/commands/payments.test.ts +241 -0
  390. package/src/modules/billing/__tests__/commands/runsDelete.test.ts +146 -0
  391. package/src/modules/billing/__tests__/commands/runsPostDrafts.test.ts +106 -0
  392. package/src/modules/billing/__tests__/commands/topups.test.ts +11 -0
  393. package/src/modules/billing/__tests__/entityRegistry.test.ts +326 -0
  394. package/src/modules/billing/__tests__/events.test.ts +142 -0
  395. package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +28 -11
  396. package/src/modules/billing/__tests__/lib/accountFinancials.test.ts +53 -0
  397. package/src/modules/billing/__tests__/lib/accountSummary.test.ts +97 -0
  398. package/src/modules/billing/__tests__/lib/allocation.test.ts +319 -0
  399. package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +231 -20
  400. package/src/modules/billing/__tests__/lib/billingSettings.test.ts +109 -0
  401. package/src/modules/billing/__tests__/lib/customerLookup.test.ts +156 -0
  402. package/src/modules/billing/__tests__/lib/dueDate.test.ts +35 -0
  403. package/src/modules/billing/__tests__/lib/iban.test.ts +38 -0
  404. package/src/modules/billing/__tests__/lib/invoiceDelivery.test.ts +189 -0
  405. package/src/modules/billing/__tests__/lib/invoiceWriter.test.ts +212 -10
  406. package/src/modules/billing/__tests__/lib/itemBillingState.test.ts +42 -0
  407. package/src/modules/billing/__tests__/lib/itemPriceChange.test.ts +165 -0
  408. package/src/modules/billing/__tests__/lib/lineAmounts.test.ts +86 -0
  409. package/src/modules/billing/__tests__/lib/navigation.test.ts +71 -0
  410. package/src/modules/billing/__tests__/lib/numbering.test.ts +91 -0
  411. package/src/modules/billing/__tests__/lib/overdue.test.ts +36 -0
  412. package/src/modules/billing/__tests__/lib/paymentCsv.test.ts +148 -0
  413. package/src/modules/billing/__tests__/lib/paymentImport.test.ts +113 -0
  414. package/src/modules/billing/__tests__/lib/paymentsSample.test.ts +73 -0
  415. package/src/modules/billing/__tests__/lib/pdf/invoicePdf.test.ts +199 -0
  416. package/src/modules/billing/__tests__/lib/pdf/invoicePdfService.test.ts +147 -0
  417. package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +374 -98
  418. package/src/modules/billing/__tests__/lib/prepaidTaxPoint.test.ts +118 -0
  419. package/src/modules/billing/__tests__/lib/seedsDemo.test.ts +53 -2
  420. package/src/modules/billing/__tests__/lib/sellerSettings.test.ts +28 -0
  421. package/src/modules/billing/__tests__/lib/servicePeriods.test.ts +190 -0
  422. package/src/modules/billing/__tests__/lib/topupCapture.test.ts +6 -28
  423. package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +21 -52
  424. package/src/modules/billing/__tests__/lib/vat.test.ts +51 -0
  425. package/src/modules/billing/__tests__/package-isolation.test.ts +22 -8
  426. package/src/modules/billing/__tests__/setup.test.ts +13 -4
  427. package/src/modules/billing/__tests__/validators.test.ts +193 -3
  428. package/src/modules/billing/acl.ts +12 -0
  429. package/src/modules/billing/api/accounts/[id]/summary/route.ts +62 -0
  430. package/src/modules/billing/api/accounts/route.ts +58 -4
  431. package/src/modules/billing/api/export/account/[id]/route.ts +20 -18
  432. package/src/modules/billing/api/invoices/[id]/memos/route.ts +63 -0
  433. package/src/modules/billing/api/invoices/[id]/pdf/route.ts +79 -0
  434. package/src/modules/billing/api/invoices/[id]/route.ts +57 -70
  435. package/src/modules/billing/api/invoices/[id]/send/route.ts +127 -0
  436. package/src/modules/billing/api/invoices/add-line/route.ts +3 -3
  437. package/src/modules/billing/api/invoices/delete-draft/route.ts +33 -0
  438. package/src/modules/billing/api/invoices/edit-line/route.ts +4 -4
  439. package/src/modules/billing/api/invoices/post/route.ts +7 -6
  440. package/src/modules/billing/api/invoices/remove-line/route.ts +3 -3
  441. package/src/modules/billing/api/invoices/route.ts +81 -86
  442. package/src/modules/billing/api/items/bulk/route.ts +2 -1
  443. package/src/modules/billing/api/items/change-price/route.ts +54 -0
  444. package/src/modules/billing/api/items/route.ts +23 -7
  445. package/src/modules/billing/api/payment-imports/[id]/route.ts +49 -0
  446. package/src/modules/billing/api/payment-imports/route.ts +57 -0
  447. package/src/modules/billing/api/payments/[id]/route.ts +63 -0
  448. package/src/modules/billing/api/payments/allocate/route.ts +35 -0
  449. package/src/modules/billing/api/payments/assign/route.ts +36 -0
  450. package/src/modules/billing/api/payments/convert-to-topup/route.ts +35 -0
  451. package/src/modules/billing/api/payments/ignore/route.ts +35 -0
  452. package/src/modules/billing/api/payments/import/route.ts +72 -0
  453. package/src/modules/billing/api/payments/restore/route.ts +35 -0
  454. package/src/modules/billing/api/payments/route.ts +147 -0
  455. package/src/modules/billing/api/payments/unallocate/route.ts +35 -0
  456. package/src/modules/billing/api/payments/unassign/route.ts +35 -0
  457. package/src/modules/billing/api/run-outcomes/route.ts +50 -7
  458. package/src/modules/billing/api/runs/[id]/post-drafts/route.ts +71 -0
  459. package/src/modules/billing/api/runs/[id]/route.ts +59 -0
  460. package/src/modules/billing/api/runs/retry-failed/route.ts +4 -3
  461. package/src/modules/billing/api/runs/route.ts +17 -9
  462. package/src/modules/billing/api/settings/number-preview/route.ts +46 -0
  463. package/src/modules/billing/api/settings/route.ts +116 -0
  464. package/src/modules/billing/api/statements/route.ts +5 -0
  465. package/src/modules/billing/api/test-invoices/wipe/route.ts +2 -2
  466. package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +151 -372
  467. package/src/modules/billing/backend/billing/accounts/create/page.tsx +6 -0
  468. package/src/modules/billing/backend/billing/accounts/page.tsx +71 -5
  469. package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +445 -96
  470. package/src/modules/billing/backend/billing/invoices/page.tsx +8 -227
  471. package/src/modules/billing/backend/billing/items/[id]/page.tsx +75 -6
  472. package/src/modules/billing/backend/billing/items/create/page.tsx +13 -8
  473. package/src/modules/billing/backend/billing/items/page.tsx +19 -3
  474. package/src/modules/billing/backend/billing/payments/[id]/page.meta.ts +19 -0
  475. package/src/modules/billing/backend/billing/payments/[id]/page.tsx +415 -0
  476. package/src/modules/billing/backend/billing/payments/imports/[id]/page.meta.ts +19 -0
  477. package/src/modules/billing/backend/billing/payments/imports/[id]/page.tsx +106 -0
  478. package/src/modules/billing/backend/billing/payments/page.meta.ts +16 -0
  479. package/src/modules/billing/backend/billing/payments/page.tsx +56 -0
  480. package/src/modules/billing/backend/billing/runs/[id]/page.tsx +50 -19
  481. package/src/modules/billing/backend/billing/runs/page.tsx +60 -9
  482. package/src/modules/billing/backend/billing/statements/[id]/page.tsx +87 -6
  483. package/src/modules/billing/backend/billing/statements/page.tsx +2 -2
  484. package/src/modules/billing/backend/billing/topups/page.tsx +2 -2
  485. package/src/modules/billing/backend/billing/transactions/page.tsx +2 -2
  486. package/src/modules/billing/backend/billing/usage/create/page.tsx +3 -2
  487. package/src/modules/billing/backend/config/billing/page.meta.ts +21 -0
  488. package/src/modules/billing/backend/config/billing/page.tsx +549 -0
  489. package/src/modules/billing/ce.ts +8 -7
  490. package/src/modules/billing/cli.ts +31 -1
  491. package/src/modules/billing/commands/accounts.ts +6 -0
  492. package/src/modules/billing/commands/index.ts +3 -0
  493. package/src/modules/billing/commands/invoices.ts +371 -238
  494. package/src/modules/billing/commands/items.ts +143 -0
  495. package/src/modules/billing/commands/memos.ts +218 -0
  496. package/src/modules/billing/commands/payments.ts +428 -0
  497. package/src/modules/billing/commands/runs.ts +208 -6
  498. package/src/modules/billing/commands/settings.ts +77 -0
  499. package/src/modules/billing/commands/topups.ts +12 -3
  500. package/src/modules/billing/commands/transactions.ts +2 -2
  501. package/src/modules/billing/components/AccountForm.tsx +199 -53
  502. package/src/modules/billing/components/AccountInvoicesSection.tsx +75 -0
  503. package/src/modules/billing/components/AccountPaymentsSection.tsx +42 -0
  504. package/src/modules/billing/components/AccountPicker.tsx +70 -0
  505. package/src/modules/billing/components/AllocatePaymentDialog.tsx +167 -0
  506. package/src/modules/billing/components/BillingDataTable.tsx +34 -0
  507. package/src/modules/billing/components/BillingSettingsLink.tsx +25 -0
  508. package/src/modules/billing/components/ChangeItemPriceDialog.tsx +180 -0
  509. package/src/modules/billing/components/CreateMemoDialog.tsx +232 -0
  510. package/src/modules/billing/components/CustomerLabel.tsx +53 -0
  511. package/src/modules/billing/components/InvoicesTable.tsx +344 -0
  512. package/src/modules/billing/components/ItemForm.tsx +233 -44
  513. package/src/modules/billing/components/ItemPriceField.tsx +81 -0
  514. package/src/modules/billing/components/LineFormDialog.tsx +25 -1
  515. package/src/modules/billing/components/ManualPaymentDialog.tsx +140 -0
  516. package/src/modules/billing/components/MoneyInput.tsx +26 -0
  517. package/src/modules/billing/components/PaymentUploadDialog.tsx +148 -0
  518. package/src/modules/billing/components/PaymentsTable.tsx +213 -0
  519. package/src/modules/billing/components/PostRunDraftsButton.tsx +129 -0
  520. package/src/modules/billing/components/PrepaidPanel.tsx +20 -7
  521. package/src/modules/billing/components/RunCyclesSection.tsx +298 -0
  522. package/src/modules/billing/components/TestRunActions.tsx +152 -0
  523. package/src/modules/billing/components/account/AccountItemsTab.tsx +192 -0
  524. package/src/modules/billing/components/account/AccountRunBillingDialog.tsx +147 -0
  525. package/src/modules/billing/components/account/AccountRunsTab.tsx +201 -0
  526. package/src/modules/billing/components/account/AccountStatementsTab.tsx +120 -0
  527. package/src/modules/billing/components/account/AccountSummaryCards.tsx +153 -0
  528. package/src/modules/billing/components/account/AccountUsageTab.tsx +122 -0
  529. package/src/modules/billing/components/account/AccountView.tsx +101 -0
  530. package/src/modules/billing/components/account/accountRow.ts +70 -0
  531. package/src/modules/billing/components/customerNames.ts +62 -0
  532. package/src/modules/billing/data/entities.ts +685 -15
  533. package/src/modules/billing/data/entityIds.ts +25 -11
  534. package/src/modules/billing/data/validators.ts +388 -8
  535. package/src/modules/billing/events.ts +82 -3
  536. package/src/modules/billing/i18n/en.json +457 -21
  537. package/src/modules/billing/i18n/pl.json +457 -21
  538. package/src/modules/billing/index.ts +6 -2
  539. package/src/modules/billing/lib/accountFinancials.ts +84 -0
  540. package/src/modules/billing/lib/accountLock.ts +32 -0
  541. package/src/modules/billing/lib/accountStatus.ts +11 -9
  542. package/src/modules/billing/lib/accountSummary.ts +172 -0
  543. package/src/modules/billing/lib/allocation.ts +515 -0
  544. package/src/modules/billing/lib/allocationEvents.ts +26 -0
  545. package/src/modules/billing/lib/billPeriod.ts +19 -0
  546. package/src/modules/billing/lib/billRunEngine.ts +362 -162
  547. package/src/modules/billing/lib/billRunLabel.ts +1 -1
  548. package/src/modules/billing/lib/billingSettings.ts +145 -0
  549. package/src/modules/billing/lib/commandRoute.ts +112 -0
  550. package/src/modules/billing/lib/customerLookup.ts +157 -0
  551. package/src/modules/billing/lib/dateOnly.ts +22 -0
  552. package/src/modules/billing/lib/documentParties.ts +23 -0
  553. package/src/modules/billing/lib/documentTypes.ts +37 -0
  554. package/src/modules/billing/lib/dueDate.ts +26 -0
  555. package/src/modules/billing/lib/iban.ts +36 -0
  556. package/src/modules/billing/lib/invoiceDelivery.ts +197 -0
  557. package/src/modules/billing/lib/invoiceLanguages.ts +34 -0
  558. package/src/modules/billing/lib/invoiceWriter.ts +162 -148
  559. package/src/modules/billing/lib/itemBillingState.ts +67 -0
  560. package/src/modules/billing/lib/itemPriceChange.ts +56 -0
  561. package/src/modules/billing/lib/itemSelector.ts +4 -1
  562. package/src/modules/billing/lib/lineAmounts.ts +61 -0
  563. package/src/modules/billing/lib/memoGuards.ts +41 -0
  564. package/src/modules/billing/lib/navigation.ts +64 -0
  565. package/src/modules/billing/lib/numbering.ts +174 -0
  566. package/src/modules/billing/lib/overdue.ts +60 -0
  567. package/src/modules/billing/lib/paymentCsv.ts +171 -0
  568. package/src/modules/billing/lib/paymentGateway.ts +52 -0
  569. package/src/modules/billing/lib/paymentImport.ts +228 -0
  570. package/src/modules/billing/lib/paymentMatcher.ts +88 -0
  571. package/src/modules/billing/lib/pdf/attachmentStore.ts +60 -0
  572. package/src/modules/billing/lib/pdf/fonts/NotoSans-Bold.ttf +0 -0
  573. package/src/modules/billing/lib/pdf/fonts/NotoSans-Regular.ttf +0 -0
  574. package/src/modules/billing/lib/pdf/fonts/OFL.txt +93 -0
  575. package/src/modules/billing/lib/pdf/fonts/embeddedFonts.ts +9 -0
  576. package/src/modules/billing/lib/pdf/invoiceModel.ts +328 -0
  577. package/src/modules/billing/lib/pdf/invoicePdf.ts +216 -0
  578. package/src/modules/billing/lib/pdf/invoiceTemplate.ts +338 -0
  579. package/src/modules/billing/lib/prepaidConfig.ts +27 -36
  580. package/src/modules/billing/lib/prepaidConsume.ts +29 -6
  581. package/src/modules/billing/lib/prepaidPeriodClose.ts +410 -242
  582. package/src/modules/billing/lib/prepaidTaxPoint.ts +76 -0
  583. package/src/modules/billing/lib/seeds/demo.ts +79 -6
  584. package/src/modules/billing/lib/seeds/paymentsSample.ts +136 -0
  585. package/src/modules/billing/lib/seeds/sellerSettings.ts +78 -0
  586. package/src/modules/billing/lib/servicePeriods.ts +226 -0
  587. package/src/modules/billing/lib/tenantLock.ts +1 -1
  588. package/src/modules/billing/lib/testRunCleanup.ts +42 -0
  589. package/src/modules/billing/lib/topupCapture.ts +3 -4
  590. package/src/modules/billing/lib/topupReceipt.ts +70 -103
  591. package/src/modules/billing/lib/usageRater.ts +1 -1
  592. package/src/modules/billing/lib/usageRunner.ts +17 -5
  593. package/src/modules/billing/lib/vat.ts +56 -0
  594. package/src/modules/billing/migrations/Migration20261114000000_billing_customer_kind.ts +25 -0
  595. package/src/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.ts +37 -0
  596. package/src/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.ts +132 -0
  597. package/src/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.ts +58 -0
  598. package/src/modules/billing/migrations/Migration20261117000000_billing_payments.ts +97 -0
  599. package/src/modules/billing/migrations/Migration20261118000000_billing_memos.ts +21 -0
  600. package/src/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.ts +39 -0
  601. package/src/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.ts +35 -0
  602. package/src/modules/billing/setup.ts +11 -237
  603. package/src/modules/billing/subscribers/credit-memo-auto-send.ts +15 -0
  604. package/src/modules/billing/subscribers/debit-memo-apply-credit.ts +15 -0
  605. package/src/modules/billing/subscribers/debit-memo-auto-send.ts +15 -0
  606. package/src/modules/billing/subscribers/invoice-apply-credit.ts +39 -0
  607. package/src/modules/billing/subscribers/invoice-auto-send.ts +46 -0
  608. package/src/modules/billing/subscribers/payment-captured-to-paid.ts +84 -62
  609. package/src/modules/billing/subscribers/prepaid-topup-captured.ts +4 -8
  610. package/src/modules/billing/workers/reap-stale-bill-runs.ts +4 -4
  611. package/dist/modules/billing/lib/invoiceStatus.js +0 -38
  612. package/dist/modules/billing/lib/invoiceStatus.js.map +0 -7
  613. package/src/modules/billing/__tests__/lib/invoiceStatus.test.ts +0 -97
  614. package/src/modules/billing/lib/invoiceStatus.ts +0 -61
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/billing/lib/prepaidPeriodClose.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n BillingAccount,\n BillingAccountBalance,\n BillingAccountTransaction,\n BillingItem,\n BillingStatement,\n BillRun,\n} from '../data/entities'\nimport type { BillCycle } from '../data/validators'\nimport { advanceNextBillDate, addDays, deriveBillPeriod, isCycleDue, type BillPeriod } from './billPeriod'\nimport { applyBalanceMovement } from './balanceLedger'\nimport { resolveLowBalanceThreshold } from './balanceStatus'\nimport { buildInvoiceLinesFromItems } from './invoiceWriter'\nimport { selectItemsForPeriod, type SelectableItem } from './itemSelector'\nimport { absMoney, addMoney, fromUnits, subMoney, toUnits } from './money'\nimport {\n getLowBalanceThresholdDefault,\n getNearLimitBufferDefault,\n getStatementEnabled,\n} from './prepaidConfig'\nimport { buildBalanceCrossingEvents, type DomainEvent } from './balanceEvents'\n\n/**\n * Prepaid period close (SPEC-002 P3).\n *\n * A prepaid account never gets a payable draft invoice. Instead the Bill Run,\n * for a due prepaid account:\n * 1. charges its `recurring` / `one_time` items to the **balance** (atomic\n * debit + transaction, deterministic `source_ref =\n * recurring-{itemId}-{periodEndISO}`, then `billed_to_date = period_end`),\n * 2. builds a non-fiscal **BillingStatement** (opening, topups, usage,\n * recurring, closing + per-item breakdown).\n *\n * Metered usage was already rated + debited in real time during the period \u2014\n * it is NOT re-rated here; the statement only summarizes it.\n *\n * Three anti-duplicate guards: the statement unique\n * `(bill_account_id, period_start, period_end)`, the per-item `billed_to_date`,\n * and the deterministic charge `source_ref`.\n */\n\nexport type PrepaidCloseMode = 'real' | 'test' | 'dry'\n\nexport type PrepaidAccountWarnings = {\n // Reserved for future per-close warnings (e.g. negative-balance close).\n closed_negative?: boolean\n}\n\nexport type PrepaidAccountResult =\n | {\n status: 'success' | 'success_with_warnings' | 'skipped_existing_statement'\n warnings: PrepaidAccountWarnings | null\n statementId: string | null\n cyclesEmitted: number\n statementEvents: DomainEvent[]\n }\n | { status: 'failed'; errorMessage: string }\n\nconst SCHEDULED_CYCLE_LIMIT = 1\nconst CATCH_UP_HARD_CAP = 60\n\nexport type ProcessPrepaidParams = {\n asOfDate: Date\n mode: PrepaidCloseMode\n triggeredBy: 'schedule' | 'manual'\n catchUp?: boolean\n}\n\n/**\n * Multi-cycle prepaid account processing (mirrors the postpaid engine's\n * catch-up loop). One statement + one set of recurring debits per missed\n * period \u2014 never combined.\n */\nexport async function processPrepaidAccount(\n em: EntityManager,\n account: BillingAccount,\n billRun: BillRun,\n params: ProcessPrepaidParams,\n): Promise<PrepaidAccountResult> {\n const maxCycles =\n params.triggeredBy === 'schedule' || !params.catchUp\n ? SCHEDULED_CYCLE_LIMIT\n : CATCH_UP_HARD_CAP\n\n const statementEnabled = await getStatementEnabled(em)\n // Resolve the crossing thresholds once \u2014 they don't change during the run.\n const balanceRow = await em.findOne(BillingAccountBalance, {\n tenantId: account.tenantId,\n billAccountId: account.id,\n })\n const threshold = resolveLowBalanceThreshold(\n balanceRow?.lowBalanceThreshold ?? null,\n await getLowBalanceThresholdDefault(em),\n )\n const nearLimitBuffer = await getNearLimitBufferDefault(em)\n const creditLimit = account.creditLimit ?? '0'\n\n let cyclesEmitted = 0\n let lastStatementId: string | null = null\n const statementEvents: DomainEvent[] = []\n // Chain opening \u2192 closing across cycles in a catch-up run. Null on the first\n // cycle \u2192 closePrepaidPeriod resolves it from the prior statement / ledger.\n let previousClosing: string | null = null\n\n while (cyclesEmitted < maxCycles && isCycleDue(account.nextBillDate, params.asOfDate)) {\n const period = deriveBillPeriod(account.nextBillDate, account.billCycle as BillCycle)\n\n // Statement anti-duplicate (only when we would actually persist).\n if (params.mode !== 'dry') {\n const existing = await em.findOne(BillingStatement, {\n tenantId: account.tenantId,\n billAccountId: account.id,\n periodStart: period.periodStart,\n periodEnd: period.periodEnd,\n })\n if (existing) {\n return {\n status: 'skipped_existing_statement',\n warnings: null,\n statementId: existing.id,\n cyclesEmitted,\n statementEvents,\n }\n }\n }\n\n const cycle = await closePrepaidPeriod(em, account, period, billRun, {\n mode: params.mode,\n statementEnabled,\n previousClosing,\n threshold,\n nearLimitBuffer,\n creditLimit,\n })\n previousClosing = cycle.closingBalance\n // Recurring charges move the balance too, so they can trip the same\n // low/exhausted/over_limit crossings as a usage debit (SPEC-002). These\n // are accumulated and emitted by the engine AFTER the run commits.\n statementEvents.push(...cycle.crossingEvents)\n\n // A negative closing balance is NORMAL for prepaid (the customer overspent;\n // it's cleared by the next top-up), so it is not flagged as a warning.\n if (cycle.statementId) {\n lastStatementId = cycle.statementId\n statementEvents.push({\n id: 'billing.statement.generated',\n payload: {\n statementId: cycle.statementId,\n billAccountId: account.id,\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billRunId: billRun.id,\n periodStart: period.periodStart.toISOString().slice(0, 10),\n periodEnd: period.periodEnd.toISOString().slice(0, 10),\n currencyCode: account.currencyCode,\n openingBalance: cycle.openingBalance,\n closingBalance: cycle.closingBalance,\n totalTopups: cycle.totalTopups,\n totalUsage: cycle.totalUsage,\n totalRecurring: cycle.totalRecurring,\n totalAdjustments: cycle.totalAdjustments,\n },\n })\n }\n\n if (params.mode === 'real') {\n account.nextBillDate = advanceNextBillDate(\n account.nextBillDate,\n account.billCycle as BillCycle,\n )\n account.lastBillDate = period.periodEnd\n account.updatedAt = new Date()\n await em.flush()\n } else {\n // test / dry: do not advance state; one iteration only.\n cyclesEmitted += 1\n break\n }\n cyclesEmitted += 1\n }\n\n return {\n status: 'success',\n warnings: null,\n statementId: lastStatementId,\n cyclesEmitted,\n statementEvents,\n }\n}\n\ntype ClosePeriodResult = {\n statementId: string | null\n openingBalance: string\n totalTopups: string\n totalUsage: string\n totalRecurring: string\n totalAdjustments: string\n closingBalance: string\n crossingEvents: DomainEvent[]\n}\n\ntype DebitBreakdownEntry = {\n billing_item_id: string\n billing_type: string\n uom_code?: string\n quantity: string\n amount: string\n}\n\nasync function closePrepaidPeriod(\n em: EntityManager,\n account: BillingAccount,\n period: BillPeriod,\n billRun: BillRun,\n opts: {\n mode: PrepaidCloseMode\n statementEnabled: boolean\n previousClosing: string | null\n threshold: string\n nearLimitBuffer: string\n creditLimit: string\n },\n): Promise<ClosePeriodResult> {\n const periodEndISO = period.periodEnd.toISOString().slice(0, 10)\n\n // Recurring + one_time items eligible for this period (usage excluded by the\n // selector \u2014 it was rated in real time).\n const items = await em.find(BillingItem, {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n deletedAt: null,\n } as never)\n const { included } = selectItemsForPeriod(items as unknown as SelectableItem[], period)\n const billableItems = included as unknown as BillingItem[]\n const lines = buildInvoiceLinesFromItems(billableItems)\n\n // Window bounds for categorized sums (topups / usage already in the ledger).\n const windowStart = period.periodStart\n const windowEnd = addDays(period.periodEnd, 1) // exclusive upper bound\n\n // Opening chains from the prior statement's closing \u2014 NOT a created_at sum.\n // Recurring debits are stamped at run time (after period_end), so a\n // `SUM(amount WHERE created_at < period_start)` would drop the prior period's\n // recurring charge and break the chain. Resolution order:\n // 1. previous cycle's closing (catch-up, in this run),\n // 2. the prior persisted statement's closing (month-over-month),\n // 3. the ledger balance before the first-ever statement window.\n const openingBalance = await resolveOpeningBalance(em, account, period, opts.previousClosing)\n const totalTopups = await sumByTypeInWindow(em, account, 'topup', windowStart, windowEnd)\n const totalUsage = absMoney(\n await sumByTypeInWindow(em, account, 'usage', windowStart, windowEnd),\n )\n // Manual adjustments / reversals that landed in the window \u2014 SIGNED (a credit\n // is +, a debit \u2212). Kept as a first-class term so the statement is\n // self-consistent and the opening\u2192closing chain never drifts.\n const totalAdjustments = addMoney(\n await sumByTypeInWindow(em, account, 'adjustment', windowStart, windowEnd),\n await sumByTypeInWindow(em, account, 'reversal', windowStart, windowEnd),\n )\n\n let totalRecurring = '0.0000'\n const breakdown: DebitBreakdownEntry[] = []\n const chargeTransactionIds: string[] = []\n // The balance just before / after the recurring batch \u2014 for crossing events.\n let recurringBalanceBefore: string | null = null\n let recurringBalanceAfter: string | null = null\n\n if (opts.mode === 'real') {\n const now = new Date()\n for (const line of lines) {\n const movement = await applyBalanceMovement(em, {\n organizationId: account.organizationId,\n tenantId: account.tenantId,\n billAccountId: account.id,\n currencyCode: account.currencyCode,\n type: line.billingType === 'one_time' ? 'one_time' : 'recurring',\n amount: fromUnits(-toUnits(line.totalNetAmount)),\n description: line.description,\n billingItemId: line.billingItemId,\n sourceRef: `recurring-${line.billingItemId}-${periodEndISO}`,\n metadata: {\n bill_period_start: period.periodStart.toISOString().slice(0, 10),\n bill_period_end: periodEndISO,\n bill_run_id: billRun.id,\n },\n })\n if (recurringBalanceBefore === null) recurringBalanceBefore = movement.balanceBefore\n recurringBalanceAfter = movement.balanceAfter\n totalRecurring = addMoney(totalRecurring, line.totalNetAmount)\n if (!movement.deduplicated) chargeTransactionIds.push(movement.transaction.id)\n breakdown.push({\n billing_item_id: line.billingItemId,\n billing_type: line.billingType,\n quantity: line.quantity,\n amount: line.totalNetAmount,\n })\n const item = billableItems.find((it) => it.id === line.billingItemId)\n if (item) {\n item.billedToDate = period.periodEnd\n item.updatedAt = now\n }\n }\n await em.flush()\n } else {\n // test / dry: compute the would-be charges, persist nothing.\n for (const line of lines) {\n totalRecurring = addMoney(totalRecurring, line.totalNetAmount)\n breakdown.push({\n billing_item_id: line.billingItemId,\n billing_type: line.billingType,\n quantity: line.quantity,\n amount: line.totalNetAmount,\n })\n }\n }\n\n // closing = opening + topups + adjustments \u2212 usage \u2212 recurring.\n const closingBalance = subMoney(\n subMoney(addMoney(addMoney(openingBalance, totalTopups), totalAdjustments), totalUsage),\n totalRecurring,\n )\n\n // The recurring batch can push the balance low / exhausted / over-limit \u2014\n // emit those crossings (real mode only; dry/test never move the balance).\n const crossingEvents =\n recurringBalanceBefore !== null && recurringBalanceAfter !== null\n ? buildBalanceCrossingEvents({\n scope: {\n billAccountId: account.id,\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n currencyCode: account.currencyCode,\n },\n balanceBefore: recurringBalanceBefore,\n balanceAfter: recurringBalanceAfter,\n threshold: opts.threshold,\n creditLimit: opts.creditLimit,\n nearLimitBuffer: opts.nearLimitBuffer,\n })\n : []\n\n let statementId: string | null = null\n if (opts.mode === 'real' && opts.statementEnabled) {\n const now = new Date()\n const statement = em.create(BillingStatement, {\n organizationId: account.organizationId,\n tenantId: account.tenantId,\n billAccountId: account.id,\n billRunId: billRun.id,\n periodStart: period.periodStart,\n periodEnd: period.periodEnd,\n currencyCode: account.currencyCode,\n openingBalance,\n totalTopups,\n totalUsage,\n totalRecurring,\n totalAdjustments,\n closingBalance,\n debitBreakdown: { items: breakdown },\n status: 'generated',\n createdAt: now,\n updatedAt: now,\n })\n em.persist(statement)\n await em.flush()\n statementId = statement.id\n\n // Backfill statement_id on the charge transactions for attribution.\n if (chargeTransactionIds.length > 0) {\n await em.nativeUpdate(\n BillingAccountTransaction,\n { id: { $in: chargeTransactionIds } } as never,\n { statementId } as never,\n )\n }\n }\n\n return {\n statementId,\n openingBalance,\n totalTopups,\n totalUsage,\n totalRecurring,\n totalAdjustments,\n closingBalance,\n crossingEvents,\n }\n}\n\n/**\n * Resolve a period's opening balance by chaining from the prior statement's\n * closing (the spec's \"chains cleanly\"), with a ledger fallback for the very\n * first statement. See the call site for why a created_at sum alone is wrong.\n */\nasync function resolveOpeningBalance(\n em: EntityManager,\n account: BillingAccount,\n period: BillPeriod,\n previousClosing: string | null,\n): Promise<string> {\n if (previousClosing !== null) return previousClosing\n const prior = await em.findOne(\n BillingStatement,\n {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n periodEnd: { $lt: period.periodStart },\n } as never,\n { orderBy: { periodEnd: 'DESC' } },\n )\n if (prior) return fromUnits(toUnits(prior.closingBalance))\n // First-ever statement: the ledger balance just before the period window\n // (only top-ups/usage exist before the first close, so created_at is safe).\n return sumAmountsBefore(em, account, period.periodStart)\n}\n\n/** Balance at `before` = SUM(amount) of transactions created strictly before it. */\nasync function sumAmountsBefore(\n em: EntityManager,\n account: BillingAccount,\n before: Date,\n): Promise<string> {\n const rows = (await em.execute(\n `SELECT COALESCE(SUM(amount), 0) AS total\n FROM billing_account_transactions\n WHERE tenant_id = ? AND organization_id = ? AND bill_account_id = ?\n AND deleted_at IS NULL AND created_at < ?`,\n [account.tenantId, account.organizationId, account.id, before],\n )) as unknown as Array<{ total: string }>\n return fromUnits(toUnits(rows?.[0]?.total ?? 0))\n}\n\n/** SUM(amount) for one transaction type within [from, to). */\nasync function sumByTypeInWindow(\n em: EntityManager,\n account: BillingAccount,\n type: string,\n from: Date,\n to: Date,\n): Promise<string> {\n const rows = (await em.execute(\n `SELECT COALESCE(SUM(amount), 0) AS total\n FROM billing_account_transactions\n WHERE tenant_id = ? AND organization_id = ? AND bill_account_id = ?\n AND deleted_at IS NULL AND type = ?\n AND created_at >= ? AND created_at < ?`,\n [account.tenantId, account.organizationId, account.id, type, from, to],\n )) as unknown as Array<{ total: string }>\n return fromUnits(toUnits(rows?.[0]?.total ?? 0))\n}\n"],
5
- "mappings": "AACA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEP,SAAS,qBAAqB,SAAS,kBAAkB,kBAAmC;AAC5F,SAAS,4BAA4B;AACrC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,4BAAiD;AAC1D,SAAS,UAAU,UAAU,WAAW,UAAU,eAAe;AACjE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kCAAoD;AAsC7D,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAc1B,eAAsB,sBACpB,IACA,SACA,SACA,QAC+B;AAC/B,QAAM,YACJ,OAAO,gBAAgB,cAAc,CAAC,OAAO,UACzC,wBACA;AAEN,QAAM,mBAAmB,MAAM,oBAAoB,EAAE;AAErD,QAAM,aAAa,MAAM,GAAG,QAAQ,uBAAuB;AAAA,IACzD,UAAU,QAAQ;AAAA,IAClB,eAAe,QAAQ;AAAA,EACzB,CAAC;AACD,QAAM,YAAY;AAAA,IAChB,YAAY,uBAAuB;AAAA,IACnC,MAAM,8BAA8B,EAAE;AAAA,EACxC;AACA,QAAM,kBAAkB,MAAM,0BAA0B,EAAE;AAC1D,QAAM,cAAc,QAAQ,eAAe;AAE3C,MAAI,gBAAgB;AACpB,MAAI,kBAAiC;AACrC,QAAM,kBAAiC,CAAC;AAGxC,MAAI,kBAAiC;AAErC,SAAO,gBAAgB,aAAa,WAAW,QAAQ,cAAc,OAAO,QAAQ,GAAG;AACrF,UAAM,SAAS,iBAAiB,QAAQ,cAAc,QAAQ,SAAsB;AAGpF,QAAI,OAAO,SAAS,OAAO;AACzB,YAAM,WAAW,MAAM,GAAG,QAAQ,kBAAkB;AAAA,QAClD,UAAU,QAAQ;AAAA,QAClB,eAAe,QAAQ;AAAA,QACvB,aAAa,OAAO;AAAA,QACpB,WAAW,OAAO;AAAA,MACpB,CAAC;AACD,UAAI,UAAU;AACZ,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,aAAa,SAAS;AAAA,UACtB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,mBAAmB,IAAI,SAAS,QAAQ,SAAS;AAAA,MACnE,MAAM,OAAO;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,sBAAkB,MAAM;AAIxB,oBAAgB,KAAK,GAAG,MAAM,cAAc;AAI5C,QAAI,MAAM,aAAa;AACrB,wBAAkB,MAAM;AACxB,sBAAgB,KAAK;AAAA,QACnB,IAAI;AAAA,QACJ,SAAS;AAAA,UACP,aAAa,MAAM;AAAA,UACnB,eAAe,QAAQ;AAAA,UACvB,UAAU,QAAQ;AAAA,UAClB,gBAAgB,QAAQ;AAAA,UACxB,WAAW,QAAQ;AAAA,UACnB,aAAa,OAAO,YAAY,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,UACzD,WAAW,OAAO,UAAU,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,UACrD,cAAc,QAAQ;AAAA,UACtB,gBAAgB,MAAM;AAAA,UACtB,gBAAgB,MAAM;AAAA,UACtB,aAAa,MAAM;AAAA,UACnB,YAAY,MAAM;AAAA,UAClB,gBAAgB,MAAM;AAAA,UACtB,kBAAkB,MAAM;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,SAAS,QAAQ;AAC1B,cAAQ,eAAe;AAAA,QACrB,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AACA,cAAQ,eAAe,OAAO;AAC9B,cAAQ,YAAY,oBAAI,KAAK;AAC7B,YAAM,GAAG,MAAM;AAAA,IACjB,OAAO;AAEL,uBAAiB;AACjB;AAAA,IACF;AACA,qBAAiB;AAAA,EACnB;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACF;AAqBA,eAAe,mBACb,IACA,SACA,QACA,SACA,MAQ4B;AAC5B,QAAM,eAAe,OAAO,UAAU,YAAY,EAAE,MAAM,GAAG,EAAE;AAI/D,QAAM,QAAQ,MAAM,GAAG,KAAK,aAAa;AAAA,IACvC,UAAU,QAAQ;AAAA,IAClB,gBAAgB,QAAQ;AAAA,IACxB,eAAe,QAAQ;AAAA,IACvB,WAAW;AAAA,EACb,CAAU;AACV,QAAM,EAAE,SAAS,IAAI,qBAAqB,OAAsC,MAAM;AACtF,QAAM,gBAAgB;AACtB,QAAM,QAAQ,2BAA2B,aAAa;AAGtD,QAAM,cAAc,OAAO;AAC3B,QAAM,YAAY,QAAQ,OAAO,WAAW,CAAC;AAS7C,QAAM,iBAAiB,MAAM,sBAAsB,IAAI,SAAS,QAAQ,KAAK,eAAe;AAC5F,QAAM,cAAc,MAAM,kBAAkB,IAAI,SAAS,SAAS,aAAa,SAAS;AACxF,QAAM,aAAa;AAAA,IACjB,MAAM,kBAAkB,IAAI,SAAS,SAAS,aAAa,SAAS;AAAA,EACtE;AAIA,QAAM,mBAAmB;AAAA,IACvB,MAAM,kBAAkB,IAAI,SAAS,cAAc,aAAa,SAAS;AAAA,IACzE,MAAM,kBAAkB,IAAI,SAAS,YAAY,aAAa,SAAS;AAAA,EACzE;AAEA,MAAI,iBAAiB;AACrB,QAAM,YAAmC,CAAC;AAC1C,QAAM,uBAAiC,CAAC;AAExC,MAAI,yBAAwC;AAC5C,MAAI,wBAAuC;AAE3C,MAAI,KAAK,SAAS,QAAQ;AACxB,UAAM,MAAM,oBAAI,KAAK;AACrB,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAW,MAAM,qBAAqB,IAAI;AAAA,QAC9C,gBAAgB,QAAQ;AAAA,QACxB,UAAU,QAAQ;AAAA,QAClB,eAAe,QAAQ;AAAA,QACvB,cAAc,QAAQ;AAAA,QACtB,MAAM,KAAK,gBAAgB,aAAa,aAAa;AAAA,QACrD,QAAQ,UAAU,CAAC,QAAQ,KAAK,cAAc,CAAC;AAAA,QAC/C,aAAa,KAAK;AAAA,QAClB,eAAe,KAAK;AAAA,QACpB,WAAW,aAAa,KAAK,aAAa,IAAI,YAAY;AAAA,QAC1D,UAAU;AAAA,UACR,mBAAmB,OAAO,YAAY,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,UAC/D,iBAAiB;AAAA,UACjB,aAAa,QAAQ;AAAA,QACvB;AAAA,MACF,CAAC;AACD,UAAI,2BAA2B,KAAM,0BAAyB,SAAS;AACvE,8BAAwB,SAAS;AACjC,uBAAiB,SAAS,gBAAgB,KAAK,cAAc;AAC7D,UAAI,CAAC,SAAS,aAAc,sBAAqB,KAAK,SAAS,YAAY,EAAE;AAC7E,gBAAU,KAAK;AAAA,QACb,iBAAiB,KAAK;AAAA,QACtB,cAAc,KAAK;AAAA,QACnB,UAAU,KAAK;AAAA,QACf,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,YAAM,OAAO,cAAc,KAAK,CAAC,OAAO,GAAG,OAAO,KAAK,aAAa;AACpE,UAAI,MAAM;AACR,aAAK,eAAe,OAAO;AAC3B,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AACA,UAAM,GAAG,MAAM;AAAA,EACjB,OAAO;AAEL,eAAW,QAAQ,OAAO;AACxB,uBAAiB,SAAS,gBAAgB,KAAK,cAAc;AAC7D,gBAAU,KAAK;AAAA,QACb,iBAAiB,KAAK;AAAA,QACtB,cAAc,KAAK;AAAA,QACnB,UAAU,KAAK;AAAA,QACf,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,iBAAiB;AAAA,IACrB,SAAS,SAAS,SAAS,gBAAgB,WAAW,GAAG,gBAAgB,GAAG,UAAU;AAAA,IACtF;AAAA,EACF;AAIA,QAAM,iBACJ,2BAA2B,QAAQ,0BAA0B,OACzD,2BAA2B;AAAA,IACzB,OAAO;AAAA,MACL,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,cAAc,QAAQ;AAAA,IACxB;AAAA,IACA,eAAe;AAAA,IACf,cAAc;AAAA,IACd,WAAW,KAAK;AAAA,IAChB,aAAa,KAAK;AAAA,IAClB,iBAAiB,KAAK;AAAA,EACxB,CAAC,IACD,CAAC;AAEP,MAAI,cAA6B;AACjC,MAAI,KAAK,SAAS,UAAU,KAAK,kBAAkB;AACjD,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,YAAY,GAAG,OAAO,kBAAkB;AAAA,MAC5C,gBAAgB,QAAQ;AAAA,MACxB,UAAU,QAAQ;AAAA,MAClB,eAAe,QAAQ;AAAA,MACvB,WAAW,QAAQ;AAAA,MACnB,aAAa,OAAO;AAAA,MACpB,WAAW,OAAO;AAAA,MAClB,cAAc,QAAQ;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,EAAE,OAAO,UAAU;AAAA,MACnC,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AACD,OAAG,QAAQ,SAAS;AACpB,UAAM,GAAG,MAAM;AACf,kBAAc,UAAU;AAGxB,QAAI,qBAAqB,SAAS,GAAG;AACnC,YAAM,GAAG;AAAA,QACP;AAAA,QACA,EAAE,IAAI,EAAE,KAAK,qBAAqB,EAAE;AAAA,QACpC,EAAE,YAAY;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAOA,eAAe,sBACb,IACA,SACA,QACA,iBACiB;AACjB,MAAI,oBAAoB,KAAM,QAAO;AACrC,QAAM,QAAQ,MAAM,GAAG;AAAA,IACrB;AAAA,IACA;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,eAAe,QAAQ;AAAA,MACvB,WAAW,EAAE,KAAK,OAAO,YAAY;AAAA,IACvC;AAAA,IACA,EAAE,SAAS,EAAE,WAAW,OAAO,EAAE;AAAA,EACnC;AACA,MAAI,MAAO,QAAO,UAAU,QAAQ,MAAM,cAAc,CAAC;AAGzD,SAAO,iBAAiB,IAAI,SAAS,OAAO,WAAW;AACzD;AAGA,eAAe,iBACb,IACA,SACA,QACiB;AACjB,QAAM,OAAQ,MAAM,GAAG;AAAA,IACrB;AAAA;AAAA;AAAA;AAAA,IAIA,CAAC,QAAQ,UAAU,QAAQ,gBAAgB,QAAQ,IAAI,MAAM;AAAA,EAC/D;AACA,SAAO,UAAU,QAAQ,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;AACjD;AAGA,eAAe,kBACb,IACA,SACA,MACA,MACA,IACiB;AACjB,QAAM,OAAQ,MAAM,GAAG;AAAA,IACrB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,CAAC,QAAQ,UAAU,QAAQ,gBAAgB,QAAQ,IAAI,MAAM,MAAM,EAAE;AAAA,EACvE;AACA,SAAO,UAAU,QAAQ,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;AACjD;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n BillingAccount,\n BillingAccountBalance,\n BillingAccountTransaction,\n BillingItem,\n BillingStatement,\n BillingRun,\n} from '../data/entities'\nimport type { BillCycle } from '../data/validators'\nimport {\n addDays,\n advanceNextBillDate,\n deriveBillPeriod,\n isCycleDue,\n shiftCycles,\n type BillPeriod,\n} from './billPeriod'\nimport { applyBalanceMovement } from './balanceLedger'\nimport { resolveLowBalanceThreshold } from './balanceStatus'\nimport {\n buildInvoiceLinesFromItems,\n buildRecurringLineDescriptors,\n type InvoiceLineDescriptor,\n} from './invoiceWriter'\nimport { shouldIncludeItem, type SelectableItem } from './itemSelector'\nimport { computeRecurringLines, type RecurringSkip } from './servicePeriods'\nimport { absMoney, addMoney, fromUnits, subMoney, toUnits } from './money'\nimport {\n getLowBalanceThresholdDefault,\n getNearLimitBufferDefault,\n getPrepaidTaxPoint,\n getStatementEnabled,\n} from './prepaidConfig'\nimport {\n buildVatSummary,\n resolvePrepaidDebit,\n type PrepaidDebit,\n type PrepaidTaxPoint,\n type VatSummaryRow,\n} from './prepaidTaxPoint'\nimport { buildBalanceCrossingEvents, type DomainEvent } from './balanceEvents'\nimport { toIsoDate, toUtcMidnight } from './dateOnly'\n\n/**\n * Prepaid period close (SPEC-002 P3, reworked by Spec F).\n *\n * A prepaid account never gets a payable draft invoice. For every due bill\n * date D (catch-up up to `asOfDate`, one rule for every trigger) the run:\n * 1. charges `recurring` items per their `charge_timing` and `prorate`\n * (Spec F, F-D4 \u2014 the same `computeRecurringLines` the postpaid engine\n * uses: in advance \u2192 `[D, D + cycle \u2212 1]`, in arrears \u2192 `[D \u2212 cycle,\n * D \u2212 1]`) and `one_time` items on the first bill date whose window\n * overlaps them, debiting the balance through the org's tax point\n * (Spec F, F-D3 \u2014 `resolvePrepaidDebit`);\n * 2. builds a non-fiscal **BillingStatement** for `[D \u2212 cycle, D \u2212 1]`\n * (opening, top-ups, usage, charges taken at D, closing, per-line\n * breakdown, VAT summary).\n *\n * Metered usage was rated + debited in real time during the period \u2014 it is\n * NOT re-rated here; the statement only summarises it.\n *\n * Bill date and item watermarks advance in memory, so test and dry runs\n * simulate every due cycle exactly like a real run; only persistence differs.\n *\n * Anti-duplicate guards: the per-item `billed_to_date` watermark, the\n * deterministic charge `source_ref`, and the statement unique\n * `(bill_account_id, period_start, period_end)`. A statement that already\n * exists for a period means that period was closed: the run skips the\n * statement, still applies (idempotent) charges, and advances \u2014 it never\n * leaves the account stuck behind an existing statement.\n */\n\nexport type PrepaidCloseMode = 'real' | 'test' | 'dry'\n\nexport type PrepaidCycleStatus = 'charged' | 'previewed' | 'empty' | 'skipped_existing'\n\n/** Same shape as the postpaid `CycleRecord` (Spec F, P-D6) plus prepaid fields. */\nexport type PrepaidCycleRecord = {\n bill_date: string\n period_start: string\n period_end: string\n status: PrepaidCycleStatus\n line_count: number\n total_net: string\n /** What left the balance for this bill date (gross in `consumption` mode). */\n total_debit: string\n invoice_id: null\n statement_id: string | null\n}\n\nexport type PrepaidSkippedItem = {\n item_id: string\n reason: string\n service_period_start: string | null\n service_period_end: string | null\n}\n\nexport type PrepaidAccountWarnings = {\n skipped_items?: PrepaidSkippedItem[]\n catch_up_cap_reached?: boolean\n}\n\nexport type PrepaidAccountResult =\n | {\n status: 'success' | 'success_with_warnings' | 'skipped_existing_statement'\n warnings: PrepaidAccountWarnings | null\n statementId: string | null\n cyclesEmitted: number\n cycles: PrepaidCycleRecord[]\n statementEvents: DomainEvent[]\n }\n | { status: 'failed'; errorMessage: string }\n\nconst CATCH_UP_HARD_CAP = 60\nconst SKIPPED_ITEMS_WARNING_CAP = 200\n/** Skip reasons that are normal state, not worth surfacing on the outcome. */\nconst QUIET_SKIP_REASONS = new Set(['item-inactive', 'item-soft-deleted'])\n\nexport type ProcessPrepaidParams = {\n asOfDate: Date\n mode: PrepaidCloseMode\n triggeredBy: 'schedule' | 'manual'\n}\n\ntype ChargeLine = {\n descriptor: InvoiceLineDescriptor\n amounts: PrepaidDebit\n sourceRef: string\n}\n\ntype CycleContext = {\n mode: PrepaidCloseMode\n taxPoint: PrepaidTaxPoint\n statementEnabled: boolean\n threshold: string\n nearLimitBuffer: string\n creditLimit: string\n}\n\nfunction chargeSourceRef(line: InvoiceLineDescriptor, kind: 'charge' | 'credit'): string {\n if (line.billingType === 'one_time') return `one_time-${line.billingItemId}`\n const start = line.servicePeriodStart ? toIsoDate(line.servicePeriodStart) : 'na'\n const end = line.servicePeriodEnd ? toIsoDate(line.servicePeriodEnd) : 'na'\n return `recurring-${line.billingItemId}-${kind}-${start}-${end}`\n}\n\nfunction money4(value: number): string {\n return fromUnits(Math.round(value * 10000))\n}\n\nfunction toSkipWarning(skip: RecurringSkip): PrepaidSkippedItem {\n return {\n item_id: skip.billingItemId,\n reason: skip.reason,\n service_period_start: skip.servicePeriodStart ? toIsoDate(skip.servicePeriodStart) : null,\n service_period_end: skip.servicePeriodEnd ? toIsoDate(skip.servicePeriodEnd) : null,\n }\n}\n\nfunction toCharge(descriptor: InvoiceLineDescriptor, kind: 'charge' | 'credit', taxPoint: PrepaidTaxPoint): ChargeLine {\n return {\n descriptor,\n amounts: resolvePrepaidDebit({\n lineAmount: descriptor.lineAmount,\n priceBasis: descriptor.priceBasis,\n taxRate: descriptor.taxRate,\n taxPoint,\n }),\n sourceRef: chargeSourceRef(descriptor, kind),\n }\n}\n\nexport async function processPrepaidAccount(\n em: EntityManager,\n account: BillingAccount,\n billRun: BillingRun,\n params: ProcessPrepaidParams,\n): Promise<PrepaidAccountResult> {\n const scope = { tenantId: account.tenantId, organizationId: account.organizationId }\n const cycle = account.billCycle as BillCycle\n const balanceRow = await em.findOne(BillingAccountBalance, {\n tenantId: account.tenantId,\n billAccountId: account.id,\n })\n const ctx: CycleContext = {\n mode: params.mode,\n taxPoint: await getPrepaidTaxPoint(em, scope),\n statementEnabled: await getStatementEnabled(em, scope),\n threshold: resolveLowBalanceThreshold(\n balanceRow?.lowBalanceThreshold ?? null,\n await getLowBalanceThresholdDefault(em, scope),\n ),\n nearLimitBuffer: await getNearLimitBufferDefault(em, scope),\n creditLimit: account.creditLimit ?? '0',\n }\n\n const items = (await em.find(BillingItem, {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n deletedAt: null,\n } as never)) as BillingItem[]\n const recurringItems = items.filter((it) => it.type === 'recurring')\n const oneTimeItems = items.filter((it) => it.type === 'one_time')\n\n // In-memory cycle state \u2014 persisted per cycle only in real mode.\n const watermarks = new Map<string, Date | null>(\n recurringItems.map((it) => [it.id, it.billedToDate ? toUtcMidnight(it.billedToDate) : null]),\n )\n const billedOneTimes = new Set<string>(oneTimeItems.filter((it) => it.billedToDate).map((it) => it.id))\n let billDate = toUtcMidnight(account.nextBillDate)\n // Chain opening \u2192 closing across cycles. Null on the first cycle \u2192\n // resolved from the prior statement / ledger.\n let previousClosing: string | null = null\n\n const cycles: PrepaidCycleRecord[] = []\n const skippedItems: PrepaidSkippedItem[] = []\n const warnings: PrepaidAccountWarnings = {}\n const statementEvents: DomainEvent[] = []\n let lastStatementId: string | null = null\n\n const pushSkips = (skips: readonly RecurringSkip[]) => {\n for (const skip of skips) {\n if (QUIET_SKIP_REASONS.has(skip.reason)) continue\n if (skippedItems.length < SKIPPED_ITEMS_WARNING_CAP) skippedItems.push(toSkipWarning(skip))\n }\n }\n\n while (isCycleDue(billDate, params.asOfDate)) {\n if (cycles.length >= CATCH_UP_HARD_CAP) {\n warnings.catch_up_cap_reached = true\n break\n }\n const nextBillDate = advanceNextBillDate(billDate, cycle)\n const period = deriveBillPeriod(billDate, cycle)\n\n // \u2500\u2500 Recurring: service periods against the in-memory watermarks \u2500\u2500\n const charges: ChargeLine[] = []\n const watermarkUpdates = new Map<string, Date | null>()\n for (const item of recurringItems) {\n const unitPrice = (item.rateJson as { unit_price?: number } | null)?.unit_price\n if (typeof unitPrice !== 'number') continue\n const computed = computeRecurringLines(\n {\n id: item.id,\n chargeTiming: item.chargeTiming ?? 'in_advance',\n prorate: item.prorate ?? true,\n billStartDate: item.billStartDate,\n billEndDate: item.billEndDate ?? null,\n billedToDate: watermarks.get(item.id) ?? null,\n unitPrice,\n isActive: item.isActive,\n deletedAt: item.deletedAt ?? null,\n },\n { billDate, cycle },\n )\n pushSkips(computed.skipped)\n if (computed.newBilledToDate?.getTime() !== watermarks.get(item.id)?.getTime()) {\n watermarkUpdates.set(item.id, computed.newBilledToDate)\n }\n const descriptors = buildRecurringLineDescriptors(item, computed.lines)\n descriptors.forEach((descriptor, index) => {\n charges.push(toCharge(descriptor, computed.lines[index]?.kind ?? 'charge', ctx.taxPoint))\n })\n }\n\n // \u2500\u2500 One-time: first bill date whose [D \u2212 cycle, D + cycle \u2212 1] window\n // overlaps (same rule as postpaid) \u2500\u2500\n const oneTimeWindow: BillPeriod = {\n periodStart: shiftCycles(billDate, cycle, -1),\n periodEnd: addDays(nextBillDate, -1),\n }\n const oneTimesThisCycle = oneTimeItems.filter(\n (it) => !billedOneTimes.has(it.id) && shouldIncludeItem(it as SelectableItem, oneTimeWindow).include,\n )\n for (const descriptor of buildInvoiceLinesFromItems(oneTimesThisCycle)) {\n charges.push(toCharge(descriptor, 'charge', ctx.taxPoint))\n }\n\n const closed = await closePrepaidPeriod(em, account, billRun, { billDate, period, charges, previousClosing }, ctx)\n previousClosing = closed.closingBalance\n statementEvents.push(...closed.events)\n if (closed.statementId) lastStatementId = closed.statementId\n cycles.push(closed.record)\n\n // Advance the in-memory state (every mode); persist in real mode only.\n for (const [itemId, watermark] of watermarkUpdates) watermarks.set(itemId, watermark)\n for (const item of oneTimesThisCycle) billedOneTimes.add(item.id)\n if (params.mode === 'real') {\n const now = new Date()\n for (const item of recurringItems) {\n if (!watermarkUpdates.has(item.id)) continue\n item.billedToDate = watermarkUpdates.get(item.id) ?? null\n item.updatedAt = now\n }\n for (const item of oneTimesThisCycle) {\n item.billedToDate = period.periodEnd\n item.updatedAt = now\n }\n account.nextBillDate = nextBillDate\n account.lastBillDate = period.periodEnd\n account.updatedAt = now\n await em.flush()\n }\n billDate = nextBillDate\n }\n\n const finalWarnings: PrepaidAccountWarnings = {\n ...warnings,\n ...(skippedItems.length ? { skipped_items: skippedItems } : {}),\n }\n return {\n status: warnings.catch_up_cap_reached ? 'success_with_warnings' : 'success',\n warnings: Object.keys(finalWarnings).length ? finalWarnings : null,\n statementId: lastStatementId,\n cyclesEmitted: cycles.filter((c) => c.status === 'charged' || c.status === 'previewed').length,\n cycles,\n statementEvents,\n }\n}\n\ntype ClosePeriodInput = {\n billDate: Date\n period: BillPeriod\n charges: ChargeLine[]\n previousClosing: string | null\n}\n\ntype ClosePeriodResult = {\n record: PrepaidCycleRecord\n /** The statement CREATED by this close (null when skipped or not written). */\n statementId: string | null\n closingBalance: string\n events: DomainEvent[]\n}\n\ntype DebitBreakdownEntry = {\n billing_item_id: string\n billing_type: string\n description: string\n quantity: string\n /** What left the balance (kept as `amount` for older statement readers). */\n amount: string\n net: string\n tax: string\n tax_rate: string\n service_period_start: string | null\n service_period_end: string | null\n proration: unknown\n}\n\nasync function closePrepaidPeriod(\n em: EntityManager,\n account: BillingAccount,\n billRun: BillingRun,\n input: ClosePeriodInput,\n ctx: CycleContext,\n): Promise<ClosePeriodResult> {\n const { period, charges } = input\n const periodStartIso = toIsoDate(period.periodStart)\n const periodEndIso = toIsoDate(period.periodEnd)\n\n // Statement anti-duplicate (real only \u2014 test / dry never write statements).\n // Date-only parameters go as ISO strings (P-D5).\n const existingStatement =\n ctx.mode === 'real'\n ? await em.findOne(BillingStatement, {\n tenantId: account.tenantId,\n billAccountId: account.id,\n periodStart: periodStartIso,\n periodEnd: periodEndIso,\n } as never)\n : null\n\n // Ledger window of this statement: (previous bill date, this bill date] \u2014\n // i.e. from the day after the previous bill date up to and including the\n // bill date D itself. Charges are taken AT D, so money that arrives on D\n // (a top-up made on the bill date) belongs to the same statement as the\n // charges \u2014 never an opening balance that pretends the account was short.\n // Consecutive windows stay contiguous: [D_prev + 1, D + 1).\n const windowStart = addDays(period.periodStart, 1)\n const windowEnd = addDays(input.billDate, 1) // exclusive upper bound\n const openingBalance = await resolveOpeningBalance(em, account, windowStart, period, input.previousClosing)\n const totalTopups = await sumByTypeInWindow(em, account, 'topup', windowStart, windowEnd)\n const totalUsage = absMoney(await sumByTypeInWindow(em, account, 'usage', windowStart, windowEnd))\n // Manual adjustments / reversals in the window \u2014 SIGNED. A first-class term\n // so the statement stays self-consistent and the chain never drifts.\n const totalAdjustments = addMoney(\n await sumByTypeInWindow(em, account, 'adjustment', windowStart, windowEnd),\n await sumByTypeInWindow(em, account, 'reversal', windowStart, windowEnd),\n )\n\n let totalRecurring = '0.0000'\n let totalNet = '0.0000'\n const breakdown: DebitBreakdownEntry[] = []\n const chargeTransactionIds: string[] = []\n let balanceBefore: string | null = null\n let balanceAfter: string | null = null\n\n for (const charge of charges) {\n const { descriptor, amounts } = charge\n const debit = money4(amounts.debit)\n totalRecurring = addMoney(totalRecurring, debit)\n totalNet = addMoney(totalNet, money4(amounts.net))\n const metadata = {\n bill_date: toIsoDate(input.billDate),\n bill_period_start: periodStartIso,\n bill_period_end: periodEndIso,\n bill_run_id: billRun.id,\n service_period_start: descriptor.servicePeriodStart ? toIsoDate(descriptor.servicePeriodStart) : null,\n service_period_end: descriptor.servicePeriodEnd ? toIsoDate(descriptor.servicePeriodEnd) : null,\n net: money4(amounts.net),\n tax: money4(amounts.tax),\n tax_rate: String(amounts.taxRate),\n tax_point: ctx.taxPoint,\n ...(descriptor.metadataExtra ?? {}),\n }\n breakdown.push({\n billing_item_id: descriptor.billingItemId,\n billing_type: descriptor.billingType,\n description: descriptor.description,\n quantity: descriptor.quantity,\n amount: debit,\n net: metadata.net,\n tax: metadata.tax,\n tax_rate: metadata.tax_rate,\n service_period_start: metadata.service_period_start,\n service_period_end: metadata.service_period_end,\n proration: (descriptor.metadataExtra as { proration?: unknown } | undefined)?.proration ?? null,\n })\n if (ctx.mode !== 'real') continue\n const movement = await applyBalanceMovement(em, {\n organizationId: account.organizationId,\n tenantId: account.tenantId,\n billAccountId: account.id,\n currencyCode: account.currencyCode,\n // Credits back (an item ended inside a charged period) stay typed as the\n // charge they reverse: adjustments are summed by date window on\n // statements and would be counted twice.\n type: descriptor.billingType === 'one_time' ? 'one_time' : 'recurring',\n amount: fromUnits(-toUnits(debit)),\n description: descriptor.description,\n billingItemId: descriptor.billingItemId,\n sourceRef: charge.sourceRef,\n metadata,\n })\n if (balanceBefore === null) balanceBefore = movement.balanceBefore\n balanceAfter = movement.balanceAfter\n if (!movement.deduplicated) chargeTransactionIds.push(movement.transaction.id)\n }\n if (ctx.mode === 'real' && charges.length > 0) await em.flush()\n\n // closing = opening + top-ups + adjustments \u2212 usage \u2212 charges.\n const closingBalance = subMoney(\n subMoney(addMoney(addMoney(openingBalance, totalTopups), totalAdjustments), totalUsage),\n totalRecurring,\n )\n const vatSummary: VatSummaryRow[] = buildVatSummary(\n charges.map((c) => c.amounts),\n ctx.taxPoint,\n )\n\n const events: DomainEvent[] =\n balanceBefore !== null && balanceAfter !== null\n ? buildBalanceCrossingEvents({\n scope: {\n billAccountId: account.id,\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n currencyCode: account.currencyCode,\n },\n balanceBefore,\n balanceAfter,\n threshold: ctx.threshold,\n creditLimit: ctx.creditLimit,\n nearLimitBuffer: ctx.nearLimitBuffer,\n })\n : []\n\n let createdStatementId: string | null = null\n if (ctx.mode === 'real' && ctx.statementEnabled && !existingStatement) {\n const now = new Date()\n const statement = em.create(BillingStatement, {\n organizationId: account.organizationId,\n tenantId: account.tenantId,\n billAccountId: account.id,\n billRunId: billRun.id,\n periodStart: period.periodStart,\n periodEnd: period.periodEnd,\n currencyCode: account.currencyCode,\n openingBalance,\n totalTopups,\n totalUsage,\n totalRecurring,\n totalAdjustments,\n closingBalance,\n debitBreakdown: { items: breakdown, bill_date: toIsoDate(input.billDate), tax_point: ctx.taxPoint },\n vatSummary,\n status: 'generated',\n createdAt: now,\n updatedAt: now,\n })\n em.persist(statement)\n await em.flush()\n createdStatementId = statement.id\n if (chargeTransactionIds.length > 0) {\n await em.nativeUpdate(\n BillingAccountTransaction,\n { id: { $in: chargeTransactionIds } } as never,\n { statementId: createdStatementId } as never,\n )\n }\n events.push({\n id: 'billing.statement.generated',\n payload: {\n statementId: createdStatementId,\n billAccountId: account.id,\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billRunId: billRun.id,\n periodStart: periodStartIso,\n periodEnd: periodEndIso,\n currencyCode: account.currencyCode,\n openingBalance,\n closingBalance,\n totalTopups,\n totalUsage,\n totalRecurring,\n totalAdjustments,\n },\n })\n }\n\n let status: PrepaidCycleStatus\n if (ctx.mode !== 'real') status = charges.length > 0 ? 'previewed' : 'empty'\n else if (existingStatement && chargeTransactionIds.length === 0) status = 'skipped_existing'\n else status = chargeTransactionIds.length > 0 ? 'charged' : 'empty'\n\n return {\n record: {\n bill_date: toIsoDate(input.billDate),\n period_start: periodStartIso,\n period_end: periodEndIso,\n status,\n line_count: charges.length,\n total_net: totalNet,\n total_debit: totalRecurring,\n invoice_id: null,\n statement_id: createdStatementId ?? existingStatement?.id ?? null,\n },\n statementId: createdStatementId,\n closingBalance,\n events,\n }\n}\n\n/**\n * Resolve a period's opening balance by chaining from the prior statement's\n * closing (the spec's \"chains cleanly\"), with a ledger fallback for the very\n * first statement. Charges are stamped at run time (after the window), so a\n * `SUM(amount WHERE created_at < period_start)` would drop the prior\n * period's charges and break the chain. Resolution order:\n * 1. previous cycle's closing (catch-up, in this run),\n * 2. the prior persisted statement's closing (month-over-month),\n * 3. the ledger balance before the first-ever statement window.\n */\nasync function resolveOpeningBalance(\n em: EntityManager,\n account: BillingAccount,\n windowStart: Date,\n period: BillPeriod,\n previousClosing: string | null,\n): Promise<string> {\n if (previousClosing !== null) return previousClosing\n const prior = await em.findOne(\n BillingStatement,\n {\n tenantId: account.tenantId,\n organizationId: account.organizationId,\n billAccountId: account.id,\n periodEnd: { $lt: toIsoDate(period.periodStart) },\n } as never,\n { orderBy: { periodEnd: 'DESC' } },\n )\n if (prior) return fromUnits(toUnits(prior.closingBalance))\n // First-ever statement: the ledger balance just before its window (only\n // top-ups / usage exist before the first close, so created_at is safe).\n return sumAmountsBefore(em, account, windowStart)\n}\n\n/** Balance at `before` = SUM(amount) of transactions created strictly before it. */\nasync function sumAmountsBefore(em: EntityManager, account: BillingAccount, before: Date): Promise<string> {\n const rows = (await em.execute(\n `SELECT COALESCE(SUM(amount), 0) AS total\n FROM billing_account_transactions\n WHERE tenant_id = ? AND organization_id = ? AND bill_account_id = ?\n AND deleted_at IS NULL AND created_at < ?`,\n [account.tenantId, account.organizationId, account.id, before],\n )) as unknown as Array<{ total: string }>\n return fromUnits(toUnits(rows?.[0]?.total ?? 0))\n}\n\n/** SUM(amount) for one transaction type within [from, to). */\nasync function sumByTypeInWindow(\n em: EntityManager,\n account: BillingAccount,\n type: string,\n from: Date,\n to: Date,\n): Promise<string> {\n const rows = (await em.execute(\n `SELECT COALESCE(SUM(amount), 0) AS total\n FROM billing_account_transactions\n WHERE tenant_id = ? AND organization_id = ? AND bill_account_id = ?\n AND deleted_at IS NULL AND type = ?\n AND created_at >= ? AND created_at < ?`,\n [account.tenantId, account.organizationId, account.id, type, from, to],\n )) as unknown as Array<{ total: string }>\n return fromUnits(toUnits(rows?.[0]?.total ?? 0))\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,37 @@
1
+ import { lineVat } from "./lineAmounts.js";
2
+ import { round2 } from "./vat.js";
3
+ function toNumber(value) {
4
+ const n = typeof value === "number" ? value : Number.parseFloat(value);
5
+ return Number.isFinite(n) ? n : 0;
6
+ }
7
+ function resolvePrepaidDebit(input) {
8
+ if (input.taxPoint === "topup") {
9
+ const amount = round2(toNumber(input.lineAmount));
10
+ return { debit: amount, net: amount, tax: 0, taxRate: 0 };
11
+ }
12
+ const rate = Math.max(0, toNumber(input.taxRate));
13
+ const vat = lineVat({ amount: input.lineAmount, priceBasis: input.priceBasis, taxRate: rate });
14
+ return { debit: vat.gross, net: vat.net, tax: vat.tax, taxRate: rate };
15
+ }
16
+ function buildVatSummary(debits, taxPoint) {
17
+ if (taxPoint === "topup") return [];
18
+ const byRate = /* @__PURE__ */ new Map();
19
+ for (const debit of debits) {
20
+ const row = byRate.get(debit.taxRate) ?? { net: 0, tax: 0, gross: 0 };
21
+ row.net = round2(row.net + debit.net);
22
+ row.tax = round2(row.tax + debit.tax);
23
+ row.gross = round2(row.gross + debit.debit);
24
+ byRate.set(debit.taxRate, row);
25
+ }
26
+ return Array.from(byRate.entries()).sort(([a], [b]) => b - a).map(([rate, row]) => ({
27
+ rate: String(rate),
28
+ net: row.net.toFixed(4),
29
+ tax: row.tax.toFixed(4),
30
+ gross: row.gross.toFixed(4)
31
+ }));
32
+ }
33
+ export {
34
+ buildVatSummary,
35
+ resolvePrepaidDebit
36
+ };
37
+ //# sourceMappingURL=prepaidTaxPoint.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/billing/lib/prepaidTaxPoint.ts"],
4
+ "sourcesContent": ["import { lineVat, type PriceBasis } from './lineAmounts'\nimport { round2 } from './vat'\n\n/**\n * Prepaid tax point (Spec F, F-D3 / P-D10) \u2014 how much a prepaid charge takes\n * from the balance. Shared by the Bill Run period close and real-time usage\n * consumption so the two paths can never drift apart.\n *\n * - `topup` (default): VAT was charged on the top-up receipt, so the\n * balance holds VAT-inclusive money and the item amount is debited as\n * entered (`debit = net = amount`, `tax = 0`). The item's VAT rate and\n * price basis do not apply.\n * - `consumption`: the receipt carried no VAT; each debit is the line's\n * gross at the item's rate and basis (`lineVat`).\n */\n\nexport type PrepaidTaxPoint = 'topup' | 'consumption'\n\nexport type PrepaidDebitInput = {\n /** Extended line amount on `priceBasis` (negative for credits). */\n lineAmount: number | string\n priceBasis: PriceBasis\n /** Item VAT rate in percent. */\n taxRate: number | string\n taxPoint: PrepaidTaxPoint\n}\n\nexport type PrepaidDebit = {\n /** What leaves the balance (positive charge, negative credit back). */\n debit: number\n net: number\n tax: number\n /** VAT rate applied (0 in `topup` mode). */\n taxRate: number\n}\n\nfunction toNumber(value: number | string): number {\n const n = typeof value === 'number' ? value : Number.parseFloat(value)\n return Number.isFinite(n) ? n : 0\n}\n\nexport function resolvePrepaidDebit(input: PrepaidDebitInput): PrepaidDebit {\n if (input.taxPoint === 'topup') {\n const amount = round2(toNumber(input.lineAmount))\n return { debit: amount, net: amount, tax: 0, taxRate: 0 }\n }\n const rate = Math.max(0, toNumber(input.taxRate))\n const vat = lineVat({ amount: input.lineAmount, priceBasis: input.priceBasis, taxRate: rate })\n return { debit: vat.gross, net: vat.net, tax: vat.tax, taxRate: rate }\n}\n\nexport type VatSummaryRow = { rate: string; net: string; tax: string; gross: string }\n\n/**\n * Per-rate VAT summary of a set of prepaid debits (statement `vat_summary`).\n * Empty when no debit carried VAT information (`topup` mode).\n */\nexport function buildVatSummary(debits: ReadonlyArray<PrepaidDebit>, taxPoint: PrepaidTaxPoint): VatSummaryRow[] {\n if (taxPoint === 'topup') return []\n const byRate = new Map<number, { net: number; tax: number; gross: number }>()\n for (const debit of debits) {\n const row = byRate.get(debit.taxRate) ?? { net: 0, tax: 0, gross: 0 }\n row.net = round2(row.net + debit.net)\n row.tax = round2(row.tax + debit.tax)\n row.gross = round2(row.gross + debit.debit)\n byRate.set(debit.taxRate, row)\n }\n return Array.from(byRate.entries())\n .sort(([a], [b]) => b - a)\n .map(([rate, row]) => ({\n rate: String(rate),\n net: row.net.toFixed(4),\n tax: row.tax.toFixed(4),\n gross: row.gross.toFixed(4),\n }))\n}\n"],
5
+ "mappings": "AAAA,SAAS,eAAgC;AACzC,SAAS,cAAc;AAmCvB,SAAS,SAAS,OAAgC;AAChD,QAAM,IAAI,OAAO,UAAU,WAAW,QAAQ,OAAO,WAAW,KAAK;AACrE,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEO,SAAS,oBAAoB,OAAwC;AAC1E,MAAI,MAAM,aAAa,SAAS;AAC9B,UAAM,SAAS,OAAO,SAAS,MAAM,UAAU,CAAC;AAChD,WAAO,EAAE,OAAO,QAAQ,KAAK,QAAQ,KAAK,GAAG,SAAS,EAAE;AAAA,EAC1D;AACA,QAAM,OAAO,KAAK,IAAI,GAAG,SAAS,MAAM,OAAO,CAAC;AAChD,QAAM,MAAM,QAAQ,EAAE,QAAQ,MAAM,YAAY,YAAY,MAAM,YAAY,SAAS,KAAK,CAAC;AAC7F,SAAO,EAAE,OAAO,IAAI,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,SAAS,KAAK;AACvE;AAQO,SAAS,gBAAgB,QAAqC,UAA4C;AAC/G,MAAI,aAAa,QAAS,QAAO,CAAC;AAClC,QAAM,SAAS,oBAAI,IAAyD;AAC5E,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,OAAO,IAAI,MAAM,OAAO,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,OAAO,EAAE;AACpE,QAAI,MAAM,OAAO,IAAI,MAAM,MAAM,GAAG;AACpC,QAAI,MAAM,OAAO,IAAI,MAAM,MAAM,GAAG;AACpC,QAAI,QAAQ,OAAO,IAAI,QAAQ,MAAM,KAAK;AAC1C,WAAO,IAAI,MAAM,SAAS,GAAG;AAAA,EAC/B;AACA,SAAO,MAAM,KAAK,OAAO,QAAQ,CAAC,EAC/B,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,EACxB,IAAI,CAAC,CAAC,MAAM,GAAG,OAAO;AAAA,IACrB,MAAM,OAAO,IAAI;AAAA,IACjB,KAAK,IAAI,IAAI,QAAQ,CAAC;AAAA,IACtB,KAAK,IAAI,IAAI,QAAQ,CAAC;AAAA,IACtB,OAAO,IAAI,MAAM,QAAQ,CAAC;AAAA,EAC5B,EAAE;AACN;",
6
+ "names": []
7
+ }
@@ -1,4 +1,5 @@
1
1
  import { BillingAccount, BillingItem, BillingAccountUsage } from "../../data/entities.js";
2
+ import { DEFAULT_DEMO_SELLER, applySellerDefaults } from "./sellerSettings.js";
2
3
  const SRC = "demo-billing";
3
4
  const ACCOUNT_NAME_PREFIX = "Demo \u2014 ";
4
5
  const ACCOUNTS = [
@@ -11,7 +12,15 @@ const ACCOUNTS = [
11
12
  },
12
13
  items: [
13
14
  { ref: `${SRC}-nw-rec`, type: "recurring", description: "Platform subscription (monthly)", unitPrice: 199 },
14
- { ref: `${SRC}-nw-usg`, type: "usage", description: "Data transfer (per GB)", unitPrice: 0.5, uomCode: "gb" }
15
+ {
16
+ ref: `${SRC}-nw-addon`,
17
+ type: "recurring",
18
+ description: "Priority support add-on (monthly)",
19
+ unitPrice: 60,
20
+ taxRate: "8",
21
+ start: "prevMonthMid"
22
+ },
23
+ { ref: `${SRC}-nw-usg`, type: "usage", description: "Data transfer (per GB)", unitPrice: 0.5, uomCode: "gb", start: "prevMonth" }
15
24
  ],
16
25
  usage: [
17
26
  { ref: `${SRC}-nw-usage`, uomCode: "gb", quantity: 120, lineDescription: "Data transfer (prev. month)" }
@@ -25,7 +34,38 @@ const ACCOUNTS = [
25
34
  invoiceLanguage: "en"
26
35
  },
27
36
  items: [
28
- { ref: `${SRC}-gx-rec`, type: "recurring", description: "Cloud plan (monthly)", unitPrice: 49 }
37
+ { ref: `${SRC}-gx-rec`, type: "recurring", description: "Cloud plan (monthly)", unitPrice: 49 },
38
+ {
39
+ ref: `${SRC}-gx-arr`,
40
+ type: "recurring",
41
+ description: "Managed backups (billed in arrears)",
42
+ unitPrice: 15,
43
+ // 0 % — shows a mixed-rate VAT summary on the document.
44
+ taxRate: "0",
45
+ chargeTiming: "in_arrears",
46
+ start: "prevMonth"
47
+ }
48
+ ],
49
+ usage: []
50
+ },
51
+ {
52
+ // B2C: a private person on a flat monthly internet plan, invoiced in Polish.
53
+ account: {
54
+ name: `${ACCOUNT_NAME_PREFIX}Jan Kowalski`,
55
+ customerId: "demo-cust-jan-kowalski",
56
+ customerKind: "person",
57
+ currencyCode: "PLN",
58
+ invoiceLanguage: "pl"
59
+ },
60
+ items: [
61
+ // B2C price quoted gross (79.00 PLN incl. 23 % VAT) — Spec F, F-D2.
62
+ {
63
+ ref: `${SRC}-jk-rec`,
64
+ type: "recurring",
65
+ description: "Internet 300 Mb/s (miesi\u0119cznie)",
66
+ unitPrice: 79,
67
+ priceBasis: "gross"
68
+ }
29
69
  ],
30
70
  usage: []
31
71
  }
@@ -50,7 +90,10 @@ async function seedBillingDemo(em, scope, options = {}) {
50
90
  const nextBillDate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
51
91
  const prevMonthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1));
52
92
  const prevMonthEnd = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0));
93
+ const prevMonthMid = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 15));
94
+ const startFor = (item) => item.start === "prevMonth" ? prevMonthStart : item.start === "prevMonthMid" ? prevMonthMid : nextBillDate;
53
95
  const result = { accountsCreated: 0, itemsCreated: 0, usageCreated: 0 };
96
+ await applySellerDefaults(em, scope, DEFAULT_DEMO_SELLER);
54
97
  for (const spec of ACCOUNTS) {
55
98
  let account = await em.findOne(BillingAccount, {
56
99
  ...scope,
@@ -61,6 +104,7 @@ async function seedBillingDemo(em, scope, options = {}) {
61
104
  account = em.create(BillingAccount, {
62
105
  ...scope,
63
106
  customerId: spec.account.customerId,
107
+ customerKind: spec.account.customerKind ?? "company",
64
108
  name: spec.account.name,
65
109
  currencyCode: spec.account.currencyCode,
66
110
  billCycle: "monthly",
@@ -85,8 +129,12 @@ async function seedBillingDemo(em, scope, options = {}) {
85
129
  ...scope,
86
130
  billAccountId: account.id,
87
131
  type: item.type,
88
- billStartDate: prevMonthStart,
132
+ billStartDate: startFor(item),
89
133
  billEndDate: null,
134
+ chargeTiming: item.chargeTiming ?? "in_advance",
135
+ prorate: true,
136
+ taxRate: item.taxRate ?? "23",
137
+ priceBasis: item.priceBasis ?? "net",
90
138
  description: item.description,
91
139
  rateJson: { unit_price: item.unitPrice },
92
140
  uomCode: item.uomCode ?? null,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/billing/lib/seeds/demo.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { BillingAccount, BillingItem, BillingAccountUsage } from '../../data/entities'\n\n/**\n * Self-contained billing test-data seed.\n *\n * Creates a couple of demo billing accounts with recurring + usage items\n * and one unrated usage record, scoped to a single tenant. Everything is\n * idempotent: accounts are matched by name, items/usage by `source_ref`,\n * so re-running is a no-op rather than a duplicate.\n *\n * The accounts are dated so they are immediately billable: `next_bill_date`\n * is the 1st of the current month, which makes a Bill Run with the default\n * (today's) as-of date produce an invoice for the previous month \u2014 the usage\n * record is seeded into exactly that period.\n *\n * Drives `mercato billing seed`. Kept free of any customer-specific data so\n * it ships with `@dainamite/billing`.\n */\n\nexport type BillingDemoScope = { tenantId: string; organizationId: string }\nexport type BillingDemoResult = {\n accountsCreated: number\n itemsCreated: number\n usageCreated: number\n}\n\n// Shared prefix for every row this seed owns \u2014 the reset path keys off it.\nconst SRC = 'demo-billing'\nconst ACCOUNT_NAME_PREFIX = 'Demo \u2014 '\n\ntype DemoAccountSpec = {\n name: string\n customerId: string\n currencyCode: string\n invoiceLanguage: string\n}\n\ntype DemoItemSpec = {\n ref: string\n type: 'recurring' | 'usage'\n description: string\n unitPrice: number\n uomCode?: string\n}\n\ntype DemoUsageSpec = {\n ref: string\n uomCode: string\n quantity: number\n lineDescription: string\n}\n\nconst ACCOUNTS: Array<{\n account: DemoAccountSpec\n items: DemoItemSpec[]\n usage: DemoUsageSpec[]\n}> = [\n {\n account: {\n name: `${ACCOUNT_NAME_PREFIX}Northwind Telecom`,\n customerId: 'demo-cust-northwind',\n currencyCode: 'PLN',\n invoiceLanguage: 'en',\n },\n items: [\n { ref: `${SRC}-nw-rec`, type: 'recurring', description: 'Platform subscription (monthly)', unitPrice: 199 },\n { ref: `${SRC}-nw-usg`, type: 'usage', description: 'Data transfer (per GB)', unitPrice: 0.5, uomCode: 'gb' },\n ],\n usage: [\n { ref: `${SRC}-nw-usage`, uomCode: 'gb', quantity: 120, lineDescription: 'Data transfer (prev. month)' },\n ],\n },\n {\n account: {\n name: `${ACCOUNT_NAME_PREFIX}Globex Cloud`,\n customerId: 'demo-cust-globex',\n currencyCode: 'EUR',\n invoiceLanguage: 'en',\n },\n items: [\n { ref: `${SRC}-gx-rec`, type: 'recurring', description: 'Cloud plan (monthly)', unitPrice: 49 },\n ],\n usage: [],\n },\n]\n\n/** Delete every row this seed owns within the scope (reset before re-seed). */\nexport async function resetBillingDemo(em: EntityManager, scope: BillingDemoScope): Promise<void> {\n await em.nativeDelete(BillingAccountUsage, {\n ...scope,\n sourceRef: { $like: `${SRC}-%` },\n } as never)\n await em.nativeDelete(BillingItem, {\n ...scope,\n sourceRef: { $like: `${SRC}-%` },\n } as never)\n await em.nativeDelete(BillingAccount, {\n ...scope,\n name: { $like: `${ACCOUNT_NAME_PREFIX}%` },\n } as never)\n}\n\nexport async function seedBillingDemo(\n em: EntityManager,\n scope: BillingDemoScope,\n options: { reset?: boolean; now?: Date } = {},\n): Promise<BillingDemoResult> {\n if (options.reset) await resetBillingDemo(em, scope)\n\n const now = options.now ?? new Date()\n // Bill period the next run will cover, given next_bill = 1st of this month:\n // periodStart = 1st of previous month, periodEnd = last day of previous month.\n // Build at UTC midnight so `date` columns don't drift a day under a positive\n // local UTC offset (local `new Date(y, m, 1)` would persist as the prev day).\n const nextBillDate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1))\n const prevMonthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1))\n const prevMonthEnd = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0))\n\n const result: BillingDemoResult = { accountsCreated: 0, itemsCreated: 0, usageCreated: 0 }\n\n for (const spec of ACCOUNTS) {\n // \u2500\u2500 Account (idempotent by name within scope) \u2500\u2500\n let account = await em.findOne(BillingAccount, {\n ...scope,\n name: spec.account.name,\n deletedAt: null,\n } as never)\n if (!account) {\n account = em.create(BillingAccount, {\n ...scope,\n customerId: spec.account.customerId,\n name: spec.account.name,\n currencyCode: spec.account.currencyCode,\n billCycle: 'monthly',\n billCycleAnchor: '1',\n invoiceEmail: `billing@${spec.account.customerId}.demo`,\n invoiceLanguage: spec.account.invoiceLanguage,\n taxId: null,\n invoiceAddress: { line1: 'ul. Demo 1', city: 'Warszawa', postal_code: '00-001', country: 'PL' },\n nextBillDate,\n lastBillDate: null,\n isActive: true,\n })\n em.persist(account)\n await em.flush() // need the id for child rows\n result.accountsCreated += 1\n }\n\n // \u2500\u2500 Items (idempotent by source_ref) \u2500\u2500\n for (const item of spec.items) {\n const existing = await em.findOne(BillingItem, { ...scope, sourceRef: item.ref } as never)\n if (existing) continue\n em.persist(\n em.create(BillingItem, {\n ...scope,\n billAccountId: account.id,\n type: item.type,\n billStartDate: prevMonthStart,\n billEndDate: null,\n description: item.description,\n rateJson: { unit_price: item.unitPrice },\n uomCode: item.uomCode ?? null,\n subscriptionId: null,\n subscriptionItemId: null,\n sourceRef: item.ref,\n currencyMismatch: false,\n billedToDate: null,\n isActive: true,\n }),\n )\n result.itemsCreated += 1\n }\n\n // \u2500\u2500 Usage records (idempotent by source_ref) \u2500\u2500\n for (const u of spec.usage) {\n const existing = await em.findOne(BillingAccountUsage, { ...scope, sourceRef: u.ref } as never)\n if (existing) continue\n em.persist(\n em.create(BillingAccountUsage, {\n ...scope,\n billAccountId: account.id,\n uomCode: u.uomCode,\n quantity: u.quantity.toFixed(4),\n periodStart: prevMonthStart,\n periodEnd: prevMonthEnd,\n lineDescription: u.lineDescription,\n sourceRef: u.ref,\n ratedInBillRunId: null,\n isActive: true,\n }),\n )\n result.usageCreated += 1\n }\n }\n\n await em.flush()\n return result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { BillingAccount, BillingItem, BillingAccountUsage } from '../../data/entities'\nimport type { CustomerKind } from '../../data/validators'\nimport type { InvoiceLanguageCode } from '../invoiceLanguages'\nimport { DEFAULT_DEMO_SELLER, applySellerDefaults } from './sellerSettings'\n\n/**\n * Self-contained billing test-data seed.\n *\n * Creates a couple of demo billing accounts with recurring + usage items\n * and one unrated usage record, scoped to a single tenant. Everything is\n * idempotent: accounts are matched by name, items/usage by `source_ref`,\n * so re-running is a no-op rather than a duplicate.\n *\n * The accounts are dated so they are immediately billable: `next_bill_date`\n * is the 1st of the current month. A Bill Run with the default (today's)\n * as-of date then shows every Spec A behaviour on one invoice per account:\n * - recurring items billed IN ADVANCE for this month (the default);\n * - Globex's in-arrears item billed for last month;\n * - Northwind's mid-cycle add-on: a prorated stub for the second half of\n * last month plus this month;\n * - usage (always in arrears) for last month.\n *\n * Drives `mercato billing seed`. Kept free of any customer-specific data so\n * it ships with `@dainamite/billing`.\n */\n\nexport type BillingDemoScope = { tenantId: string; organizationId: string }\nexport type BillingDemoResult = {\n accountsCreated: number\n itemsCreated: number\n usageCreated: number\n}\n\n// Shared prefix for every row this seed owns \u2014 the reset path keys off it.\nconst SRC = 'demo-billing'\nconst ACCOUNT_NAME_PREFIX = 'Demo \u2014 '\n\ntype DemoAccountSpec = {\n name: string\n customerId: string\n // Spec E: 'person' = a B2C account. Omitted \u2192 'company'.\n customerKind?: CustomerKind\n currencyCode: string\n invoiceLanguage: InvoiceLanguageCode\n}\n\ntype DemoItemSpec = {\n ref: string\n type: 'recurring' | 'usage'\n description: string\n unitPrice: number\n uomCode?: string\n // Spec B: VAT %. Default 23 (PL standard rate).\n taxRate?: string\n // Spec A. Default 'in_advance'.\n chargeTiming?: 'in_advance' | 'in_arrears'\n // Spec F. Default 'net'. Consumer (B2C) prices are quoted gross.\n priceBasis?: 'net' | 'gross'\n // When the item starts, relative to the seeded bill date:\n // 'billDate' (default) \u2014 1st of this month, billed in advance from day one;\n // 'prevMonth' \u2014 1st of last month (in-arrears items bill it now);\n // 'prevMonthMid' \u2014 15th of last month: an add-on bought mid-cycle,\n // so the first run shows a prorated stub line.\n start?: 'billDate' | 'prevMonth' | 'prevMonthMid'\n}\n\ntype DemoUsageSpec = {\n ref: string\n uomCode: string\n quantity: number\n lineDescription: string\n}\n\nconst ACCOUNTS: Array<{\n account: DemoAccountSpec\n items: DemoItemSpec[]\n usage: DemoUsageSpec[]\n}> = [\n {\n account: {\n name: `${ACCOUNT_NAME_PREFIX}Northwind Telecom`,\n customerId: 'demo-cust-northwind',\n currencyCode: 'PLN',\n invoiceLanguage: 'en',\n },\n items: [\n { ref: `${SRC}-nw-rec`, type: 'recurring', description: 'Platform subscription (monthly)', unitPrice: 199 },\n {\n ref: `${SRC}-nw-addon`,\n type: 'recurring',\n description: 'Priority support add-on (monthly)',\n unitPrice: 60,\n taxRate: '8',\n start: 'prevMonthMid',\n },\n { ref: `${SRC}-nw-usg`, type: 'usage', description: 'Data transfer (per GB)', unitPrice: 0.5, uomCode: 'gb', start: 'prevMonth' },\n ],\n usage: [\n { ref: `${SRC}-nw-usage`, uomCode: 'gb', quantity: 120, lineDescription: 'Data transfer (prev. month)' },\n ],\n },\n {\n account: {\n name: `${ACCOUNT_NAME_PREFIX}Globex Cloud`,\n customerId: 'demo-cust-globex',\n currencyCode: 'EUR',\n invoiceLanguage: 'en',\n },\n items: [\n { ref: `${SRC}-gx-rec`, type: 'recurring', description: 'Cloud plan (monthly)', unitPrice: 49 },\n {\n ref: `${SRC}-gx-arr`,\n type: 'recurring',\n description: 'Managed backups (billed in arrears)',\n unitPrice: 15,\n // 0 % \u2014 shows a mixed-rate VAT summary on the document.\n taxRate: '0',\n chargeTiming: 'in_arrears',\n start: 'prevMonth',\n },\n ],\n usage: [],\n },\n {\n // B2C: a private person on a flat monthly internet plan, invoiced in Polish.\n account: {\n name: `${ACCOUNT_NAME_PREFIX}Jan Kowalski`,\n customerId: 'demo-cust-jan-kowalski',\n customerKind: 'person',\n currencyCode: 'PLN',\n invoiceLanguage: 'pl',\n },\n items: [\n // B2C price quoted gross (79.00 PLN incl. 23 % VAT) \u2014 Spec F, F-D2.\n {\n ref: `${SRC}-jk-rec`,\n type: 'recurring',\n description: 'Internet 300 Mb/s (miesi\u0119cznie)',\n unitPrice: 79,\n priceBasis: 'gross',\n },\n ],\n usage: [],\n },\n]\n\n\n/** Delete every row this seed owns within the scope (reset before re-seed). */\nexport async function resetBillingDemo(em: EntityManager, scope: BillingDemoScope): Promise<void> {\n await em.nativeDelete(BillingAccountUsage, {\n ...scope,\n sourceRef: { $like: `${SRC}-%` },\n } as never)\n await em.nativeDelete(BillingItem, {\n ...scope,\n sourceRef: { $like: `${SRC}-%` },\n } as never)\n await em.nativeDelete(BillingAccount, {\n ...scope,\n name: { $like: `${ACCOUNT_NAME_PREFIX}%` },\n } as never)\n}\n\nexport async function seedBillingDemo(\n em: EntityManager,\n scope: BillingDemoScope,\n options: { reset?: boolean; now?: Date } = {},\n): Promise<BillingDemoResult> {\n if (options.reset) await resetBillingDemo(em, scope)\n\n const now = options.now ?? new Date()\n // Bill period the next run will cover, given next_bill = 1st of this month:\n // periodStart = 1st of previous month, periodEnd = last day of previous month.\n // Build at UTC midnight so `date` columns don't drift a day under a positive\n // local UTC offset (local `new Date(y, m, 1)` would persist as the prev day).\n const nextBillDate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1))\n const prevMonthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1))\n const prevMonthEnd = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0))\n const prevMonthMid = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 15))\n const startFor = (item: DemoItemSpec): Date =>\n item.start === 'prevMonth' ? prevMonthStart : item.start === 'prevMonthMid' ? prevMonthMid : nextBillDate\n\n const result: BillingDemoResult = { accountsCreated: 0, itemsCreated: 0, usageCreated: 0 }\n\n // \u2500\u2500 Seller settings (Spec B: posting needs them). Only empty fields are\n // filled, so an operator's real details are never overwritten. \u2500\u2500\n await applySellerDefaults(em, scope, DEFAULT_DEMO_SELLER)\n\n for (const spec of ACCOUNTS) {\n // \u2500\u2500 Account (idempotent by name within scope) \u2500\u2500\n let account = await em.findOne(BillingAccount, {\n ...scope,\n name: spec.account.name,\n deletedAt: null,\n } as never)\n if (!account) {\n account = em.create(BillingAccount, {\n ...scope,\n customerId: spec.account.customerId,\n customerKind: spec.account.customerKind ?? 'company',\n name: spec.account.name,\n currencyCode: spec.account.currencyCode,\n billCycle: 'monthly',\n billCycleAnchor: '1',\n invoiceEmail: `billing@${spec.account.customerId}.demo`,\n invoiceLanguage: spec.account.invoiceLanguage,\n taxId: null,\n invoiceAddress: { line1: 'ul. Demo 1', city: 'Warszawa', postal_code: '00-001', country: 'PL' },\n nextBillDate,\n lastBillDate: null,\n isActive: true,\n })\n em.persist(account)\n await em.flush() // need the id for child rows\n result.accountsCreated += 1\n }\n\n // \u2500\u2500 Items (idempotent by source_ref) \u2500\u2500\n for (const item of spec.items) {\n const existing = await em.findOne(BillingItem, { ...scope, sourceRef: item.ref } as never)\n if (existing) continue\n em.persist(\n em.create(BillingItem, {\n ...scope,\n billAccountId: account.id,\n type: item.type,\n billStartDate: startFor(item),\n billEndDate: null,\n chargeTiming: item.chargeTiming ?? 'in_advance',\n prorate: true,\n taxRate: item.taxRate ?? '23',\n priceBasis: item.priceBasis ?? 'net',\n description: item.description,\n rateJson: { unit_price: item.unitPrice },\n uomCode: item.uomCode ?? null,\n subscriptionId: null,\n subscriptionItemId: null,\n sourceRef: item.ref,\n currencyMismatch: false,\n billedToDate: null,\n isActive: true,\n }),\n )\n result.itemsCreated += 1\n }\n\n // \u2500\u2500 Usage records (idempotent by source_ref) \u2500\u2500\n for (const u of spec.usage) {\n const existing = await em.findOne(BillingAccountUsage, { ...scope, sourceRef: u.ref } as never)\n if (existing) continue\n em.persist(\n em.create(BillingAccountUsage, {\n ...scope,\n billAccountId: account.id,\n uomCode: u.uomCode,\n quantity: u.quantity.toFixed(4),\n periodStart: prevMonthStart,\n periodEnd: prevMonthEnd,\n lineDescription: u.lineDescription,\n sourceRef: u.ref,\n ratedInBillRunId: null,\n isActive: true,\n }),\n )\n result.usageCreated += 1\n }\n }\n\n await em.flush()\n return result\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,93 @@
1
+ import { PAYMENT_CSV_HEADER } from "../paymentCsv.js";
2
+ function csvField(value) {
3
+ return /[",\n]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value;
4
+ }
5
+ function money(value) {
6
+ return value.toFixed(2);
7
+ }
8
+ function buildPaymentsSampleCsv(params) {
9
+ const { invoices, receipt, date, debitMemo } = params;
10
+ const rows = [];
11
+ const missing = [];
12
+ const gross = (inv) => Number.parseFloat(inv.totalGross);
13
+ const [a, b, c] = invoices;
14
+ const rest = invoices.slice(3);
15
+ let d;
16
+ let e;
17
+ for (let i = 0; i < rest.length && !d; i += 1) {
18
+ const match = rest.slice(i + 1).find((other) => other.accountName === rest[i].accountName && other.currencyCode === rest[i].currencyCode);
19
+ if (match) {
20
+ d = rest[i];
21
+ e = match;
22
+ }
23
+ }
24
+ if (a) rows.push([date, money(gross(a)), a.currencyCode, `Payment for ${a.number}`, a.accountName, ""]);
25
+ else missing.push("exact payment (needs 1 posted invoice)");
26
+ if (b) rows.push([date, money(gross(b) / 2), b.currencyCode, `${b.number} part 1`, b.accountName, ""]);
27
+ else missing.push("partial payment (needs 2 posted invoices)");
28
+ if (c) rows.push([date, money(gross(c) + 50), c.currencyCode, `Invoice ${c.number} + advance`, c.accountName, ""]);
29
+ else missing.push("overpayment (needs 3 posted invoices)");
30
+ if (d && e) rows.push([date, money(gross(d) + gross(e)), d.currencyCode, `${d.number}, ${e.number}`, d.accountName, ""]);
31
+ else missing.push("two invoices in one title (needs two more posted invoices of one account)");
32
+ if (a) rows.push([date, "25.00", a.currencyCode, "Thank you", a.accountName, "PL61109010140000071219812874"]);
33
+ if (receipt) rows.push([date, "100.00", receipt.currencyCode, `Top-up ${receipt.number}`, receipt.accountName, ""]);
34
+ else missing.push("prepaid top-up (needs a prepaid top-up receipt)");
35
+ if (debitMemo) {
36
+ rows.push([date, money(gross(debitMemo)), debitMemo.currencyCode, `Note ${debitMemo.number}`, debitMemo.accountName, ""]);
37
+ }
38
+ rows.push([date, "12.34", "PLN", "misc transfer", "Unknown payer", ""]);
39
+ if (a) {
40
+ const other = a.currencyCode === "EUR" ? "PLN" : "EUR";
41
+ rows.push([date, money(gross(a)), other, a.number, a.accountName, ""]);
42
+ const dup = [date, "10.00", a.currencyCode, "Duplicate line", a.accountName, ""];
43
+ rows.push(dup, [...dup]);
44
+ }
45
+ const csv = [PAYMENT_CSV_HEADER.join(","), ...rows.map((row) => row.map(csvField).join(","))].join("\n") + "\n";
46
+ return { csv, rows: rows.length, missing };
47
+ }
48
+ async function loadSampleSources(em, scope) {
49
+ const invoices = await em.execute(
50
+ `SELECT i.number, i.total_gross, i.currency_code, a.name AS account_name
51
+ FROM billing_invoices i
52
+ JOIN billing_accounts a ON a.id = i.bill_account_id
53
+ WHERE i.tenant_id = ? AND i.organization_id = ? AND i.deleted_at IS NULL AND i.test_run = false
54
+ AND i.document_type = 'invoice' AND i.status = 'posted' AND a.billing_mode = 'postpaid'
55
+ ORDER BY i.issue_date ASC, i.number ASC
56
+ LIMIT 20`,
57
+ [scope.tenantId, scope.organizationId]
58
+ );
59
+ const receipts = await em.execute(
60
+ `SELECT i.number, i.currency_code, a.name AS account_name
61
+ FROM billing_invoices i
62
+ JOIN billing_accounts a ON a.id = i.bill_account_id
63
+ WHERE i.tenant_id = ? AND i.organization_id = ? AND i.deleted_at IS NULL AND i.document_type = 'receipt'
64
+ ORDER BY i.created_at DESC
65
+ LIMIT 1`,
66
+ [scope.tenantId, scope.organizationId]
67
+ );
68
+ const debits = await em.execute(
69
+ `SELECT i.number, i.total_gross, i.currency_code, a.name AS account_name
70
+ FROM billing_invoices i
71
+ JOIN billing_accounts a ON a.id = i.bill_account_id
72
+ WHERE i.tenant_id = ? AND i.organization_id = ? AND i.deleted_at IS NULL AND i.test_run = false
73
+ AND i.document_type = 'debit_memo' AND i.status = 'posted'
74
+ ORDER BY i.issue_date ASC
75
+ LIMIT 1`,
76
+ [scope.tenantId, scope.organizationId]
77
+ );
78
+ return {
79
+ debitMemo: debits?.[0] ? { number: debits[0].number, totalGross: debits[0].total_gross, currencyCode: debits[0].currency_code, accountName: debits[0].account_name } : null,
80
+ invoices: (invoices ?? []).map((row) => ({
81
+ number: row.number,
82
+ totalGross: row.total_gross,
83
+ currencyCode: row.currency_code,
84
+ accountName: row.account_name
85
+ })),
86
+ receipt: receipts?.[0] ? { number: receipts[0].number, currencyCode: receipts[0].currency_code, accountName: receipts[0].account_name } : null
87
+ };
88
+ }
89
+ export {
90
+ buildPaymentsSampleCsv,
91
+ loadSampleSources
92
+ };
93
+ //# sourceMappingURL=paymentsSample.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/billing/lib/seeds/paymentsSample.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { PAYMENT_CSV_HEADER } from '../paymentCsv'\n\n/**\n * Demo bank statement (Spec C): a `payments-sample.csv` built from the\n * organization's real posted invoices, so numbers and amounts line up and\n * every matcher outcome shows up after one upload:\n *\n * exact amount + number \u2192 allocated, invoice paid\n * half the amount \u2192 invoice partially paid\n * amount + 50 \u2192 paid, 50 credit applied to the next open invoice\n * two numbers, sum of both \u2192 both paid\n * no reference, known payer \u2192 unmatched \u2192 assign to account\n * a top-up receipt number \u2192 unmatched (prepaid) \u2192 convert to top-up\n * junk title \u2192 unmatched (no reference)\n * other currency \u2192 unmatched (currency mismatch)\n * the same line twice \u2192 both stored (occurrence index); re-upload = duplicates\n *\n * Pure builder + a loader for the CLI (`mercato billing payments-sample`).\n */\n\nexport type SampleInvoice = {\n number: string\n totalGross: string\n currencyCode: string\n accountName: string\n}\n\nexport type SampleReceipt = { number: string; currencyCode: string; accountName: string }\n\nfunction csvField(value: string): string {\n return /[\",\\n]/.test(value) ? `\"${value.replace(/\"/g, '\"\"')}\"` : value\n}\n\nfunction money(value: number): string {\n return value.toFixed(2)\n}\n\nexport function buildPaymentsSampleCsv(params: {\n invoices: SampleInvoice[]\n receipt?: SampleReceipt | null\n /** Spec D: a posted debit memo \u2014 paid by its own number like an invoice. */\n debitMemo?: SampleInvoice | null\n date: string\n}): { csv: string; rows: number; missing: string[] } {\n const { invoices, receipt, date, debitMemo } = params\n const rows: string[][] = []\n const missing: string[] = []\n const gross = (inv: SampleInvoice) => Number.parseFloat(inv.totalGross)\n const [a, b, c] = invoices\n // Two invoices of the SAME account (references to two customers are ambiguous).\n const rest = invoices.slice(3)\n let d: SampleInvoice | undefined\n let e: SampleInvoice | undefined\n for (let i = 0; i < rest.length && !d; i += 1) {\n const match = rest.slice(i + 1).find((other) => other.accountName === rest[i].accountName && other.currencyCode === rest[i].currencyCode)\n if (match) {\n d = rest[i]\n e = match\n }\n }\n\n if (a) rows.push([date, money(gross(a)), a.currencyCode, `Payment for ${a.number}`, a.accountName, ''])\n else missing.push('exact payment (needs 1 posted invoice)')\n if (b) rows.push([date, money(gross(b) / 2), b.currencyCode, `${b.number} part 1`, b.accountName, ''])\n else missing.push('partial payment (needs 2 posted invoices)')\n if (c) rows.push([date, money(gross(c) + 50), c.currencyCode, `Invoice ${c.number} + advance`, c.accountName, ''])\n else missing.push('overpayment (needs 3 posted invoices)')\n if (d && e) rows.push([date, money(gross(d) + gross(e)), d.currencyCode, `${d.number}, ${e.number}`, d.accountName, ''])\n else missing.push('two invoices in one title (needs two more posted invoices of one account)')\n if (a) rows.push([date, '25.00', a.currencyCode, 'Thank you', a.accountName, 'PL61109010140000071219812874'])\n if (receipt) rows.push([date, '100.00', receipt.currencyCode, `Top-up ${receipt.number}`, receipt.accountName, ''])\n else missing.push('prepaid top-up (needs a prepaid top-up receipt)')\n if (debitMemo) {\n rows.push([date, money(gross(debitMemo)), debitMemo.currencyCode, `Note ${debitMemo.number}`, debitMemo.accountName, ''])\n }\n rows.push([date, '12.34', 'PLN', 'misc transfer', 'Unknown payer', ''])\n if (a) {\n const other = a.currencyCode === 'EUR' ? 'PLN' : 'EUR'\n rows.push([date, money(gross(a)), other, a.number, a.accountName, ''])\n const dup = [date, '10.00', a.currencyCode, 'Duplicate line', a.accountName, '']\n rows.push(dup, [...dup])\n }\n const csv = [PAYMENT_CSV_HEADER.join(','), ...rows.map((row) => row.map(csvField).join(','))].join('\\n') + '\\n'\n return { csv, rows: rows.length, missing }\n}\n\n/** Posted, unpaid invoices of the org (oldest first) and one top-up receipt. */\nexport async function loadSampleSources(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n): Promise<{ invoices: SampleInvoice[]; receipt: SampleReceipt | null; debitMemo: SampleInvoice | null }> {\n const invoices = (await em.execute(\n `SELECT i.number, i.total_gross, i.currency_code, a.name AS account_name\n FROM billing_invoices i\n JOIN billing_accounts a ON a.id = i.bill_account_id\n WHERE i.tenant_id = ? AND i.organization_id = ? AND i.deleted_at IS NULL AND i.test_run = false\n AND i.document_type = 'invoice' AND i.status = 'posted' AND a.billing_mode = 'postpaid'\n ORDER BY i.issue_date ASC, i.number ASC\n LIMIT 20`,\n [scope.tenantId, scope.organizationId],\n )) as unknown as Array<{ number: string; total_gross: string; currency_code: string; account_name: string }>\n const receipts = (await em.execute(\n `SELECT i.number, i.currency_code, a.name AS account_name\n FROM billing_invoices i\n JOIN billing_accounts a ON a.id = i.bill_account_id\n WHERE i.tenant_id = ? AND i.organization_id = ? AND i.deleted_at IS NULL AND i.document_type = 'receipt'\n ORDER BY i.created_at DESC\n LIMIT 1`,\n [scope.tenantId, scope.organizationId],\n )) as unknown as Array<{ number: string; currency_code: string; account_name: string }>\n const debits = (await em.execute(\n `SELECT i.number, i.total_gross, i.currency_code, a.name AS account_name\n FROM billing_invoices i\n JOIN billing_accounts a ON a.id = i.bill_account_id\n WHERE i.tenant_id = ? AND i.organization_id = ? AND i.deleted_at IS NULL AND i.test_run = false\n AND i.document_type = 'debit_memo' AND i.status = 'posted'\n ORDER BY i.issue_date ASC\n LIMIT 1`,\n [scope.tenantId, scope.organizationId],\n )) as unknown as Array<{ number: string; total_gross: string; currency_code: string; account_name: string }>\n return {\n debitMemo: debits?.[0]\n ? { number: debits[0].number, totalGross: debits[0].total_gross, currencyCode: debits[0].currency_code, accountName: debits[0].account_name }\n : null,\n invoices: (invoices ?? []).map((row) => ({\n number: row.number,\n totalGross: row.total_gross,\n currencyCode: row.currency_code,\n accountName: row.account_name,\n })),\n receipt: receipts?.[0]\n ? { number: receipts[0].number, currencyCode: receipts[0].currency_code, accountName: receipts[0].account_name }\n : null,\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,41 @@
1
+ import { getBillingSettings } from "../billingSettings.js";
2
+ const SELLER_FIELDS = ["legalName", "sellerAddress", "taxId", "bankName", "iban", "bic", "invoiceFooter"];
3
+ const DEFAULT_DEMO_SELLER = {
4
+ legalName: "Dainamite Demo sp. z o.o.",
5
+ sellerAddress: { line1: "ul. Przyk\u0142adowa 10", city: "Krak\xF3w", postal_code: "30-001", country: "PL" },
6
+ taxId: "PL1234563218",
7
+ bankName: "Demo Bank S.A.",
8
+ iban: "PL61109010140000071219812874",
9
+ bic: "WBKPPLPP",
10
+ invoiceFooter: "S\u0105d Rejonowy dla Krakowa-\u015Ar\xF3dmie\u015Bcia, KRS 0000000000. Kapita\u0142 zak\u0142adowy 5 000 z\u0142."
11
+ };
12
+ function isEmpty(value) {
13
+ if (value === null || value === void 0) return true;
14
+ if (typeof value === "string") return value.trim() === "";
15
+ if (typeof value === "object") return Object.keys(value).length === 0;
16
+ return false;
17
+ }
18
+ function sellerFieldsToFill(settings, profile) {
19
+ const out = {};
20
+ for (const field of SELLER_FIELDS) {
21
+ const value = profile[field];
22
+ if (value === void 0) continue;
23
+ if (isEmpty(settings[field])) out[field] = value;
24
+ }
25
+ return out;
26
+ }
27
+ async function applySellerDefaults(em, scope, profile) {
28
+ const settings = await getBillingSettings(em, scope);
29
+ const fill = sellerFieldsToFill(settings, profile);
30
+ const fields = Object.keys(fill);
31
+ if (fields.length === 0) return [];
32
+ Object.assign(settings, fill, { updatedAt: /* @__PURE__ */ new Date() });
33
+ await em.flush();
34
+ return fields;
35
+ }
36
+ export {
37
+ DEFAULT_DEMO_SELLER,
38
+ applySellerDefaults,
39
+ sellerFieldsToFill
40
+ };
41
+ //# sourceMappingURL=sellerSettings.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/billing/lib/seeds/sellerSettings.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { BillingSettings } from '../../data/entities'\nimport { getBillingSettings, type SettingsScope } from '../billingSettings'\n\n/**\n * Seller details for demo / seed tenants (Spec B: posting an invoice needs\n * legal name, address, tax ID and IBAN). Public seed API \u2014 demo use cases\n * (`demo_gix`, `demo_puffin`, \u2026) call `applySellerDefaults` from their own\n * `seedDefaults` with a profile of their own.\n *\n * Only EMPTY fields are filled, so an operator's real details are never\n * overwritten and re-running a seed is a no-op. Values must pass the settings\n * validators (checksum-valid IBAN, 8/11-char BIC) so the settings page can be\n * saved without touching them.\n */\n\nexport type SellerProfile = {\n legalName: string\n sellerAddress: { line1: string; line2?: string; city: string; postal_code: string; country: string }\n taxId: string\n bankName: string\n iban: string\n bic: string\n invoiceFooter?: string\n}\n\nconst SELLER_FIELDS = ['legalName', 'sellerAddress', 'taxId', 'bankName', 'iban', 'bic', 'invoiceFooter'] as const\ntype SellerField = (typeof SELLER_FIELDS)[number]\n\n/** The generic seller of the billing demo seed (primary tenant). */\nexport const DEFAULT_DEMO_SELLER: SellerProfile = {\n legalName: 'Dainamite Demo sp. z o.o.',\n sellerAddress: { line1: 'ul. Przyk\u0142adowa 10', city: 'Krak\u00F3w', postal_code: '30-001', country: 'PL' },\n taxId: 'PL1234563218',\n bankName: 'Demo Bank S.A.',\n iban: 'PL61109010140000071219812874',\n bic: 'WBKPPLPP',\n invoiceFooter: 'S\u0105d Rejonowy dla Krakowa-\u015Ar\u00F3dmie\u015Bcia, KRS 0000000000. Kapita\u0142 zak\u0142adowy 5 000 z\u0142.',\n}\n\nfunction isEmpty(value: unknown): boolean {\n if (value === null || value === undefined) return true\n if (typeof value === 'string') return value.trim() === ''\n if (typeof value === 'object') return Object.keys(value as object).length === 0\n return false\n}\n\n/** Pure: which profile values would fill which empty settings fields. */\nexport function sellerFieldsToFill(\n settings: Pick<BillingSettings, SellerField>,\n profile: SellerProfile,\n): Partial<Record<SellerField, unknown>> {\n const out: Partial<Record<SellerField, unknown>> = {}\n for (const field of SELLER_FIELDS) {\n const value = profile[field]\n if (value === undefined) continue\n if (isEmpty(settings[field])) out[field] = value\n }\n return out\n}\n\n/**\n * Fill the organization's empty seller fields from `profile` and flush.\n * Returns the names of the fields that were filled (empty when nothing changed).\n */\nexport async function applySellerDefaults(\n em: EntityManager,\n scope: SettingsScope,\n profile: SellerProfile,\n): Promise<string[]> {\n const settings = await getBillingSettings(em, scope)\n const fill = sellerFieldsToFill(settings, profile)\n const fields = Object.keys(fill)\n if (fields.length === 0) return []\n Object.assign(settings, fill, { updatedAt: new Date() })\n await em.flush()\n return fields\n}\n"],
5
+ "mappings": "AAEA,SAAS,0BAA8C;AAwBvD,MAAM,gBAAgB,CAAC,aAAa,iBAAiB,SAAS,YAAY,QAAQ,OAAO,eAAe;AAIjG,MAAM,sBAAqC;AAAA,EAChD,WAAW;AAAA,EACX,eAAe,EAAE,OAAO,2BAAsB,MAAM,aAAU,aAAa,UAAU,SAAS,KAAK;AAAA,EACnG,OAAO;AAAA,EACP,UAAU;AAAA,EACV,MAAM;AAAA,EACN,KAAK;AAAA,EACL,eAAe;AACjB;AAEA,SAAS,QAAQ,OAAyB;AACxC,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,KAAK,MAAM;AACvD,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK,KAAe,EAAE,WAAW;AAC9E,SAAO;AACT;AAGO,SAAS,mBACd,UACA,SACuC;AACvC,QAAM,MAA6C,CAAC;AACpD,aAAW,SAAS,eAAe;AACjC,UAAM,QAAQ,QAAQ,KAAK;AAC3B,QAAI,UAAU,OAAW;AACzB,QAAI,QAAQ,SAAS,KAAK,CAAC,EAAG,KAAI,KAAK,IAAI;AAAA,EAC7C;AACA,SAAO;AACT;AAMA,eAAsB,oBACpB,IACA,OACA,SACmB;AACnB,QAAM,WAAW,MAAM,mBAAmB,IAAI,KAAK;AACnD,QAAM,OAAO,mBAAmB,UAAU,OAAO;AACjD,QAAM,SAAS,OAAO,KAAK,IAAI;AAC/B,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,SAAO,OAAO,UAAU,MAAM,EAAE,WAAW,oBAAI,KAAK,EAAE,CAAC;AACvD,QAAM,GAAG,MAAM;AACf,SAAO;AACT;",
6
+ "names": []
7
+ }