@dainamite/billing 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (598) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/modules/billing/acl.js +13 -1
  3. package/dist/modules/billing/acl.js.map +2 -2
  4. package/dist/modules/billing/api/accounts/[id]/summary/route.js +52 -0
  5. package/dist/modules/billing/api/accounts/[id]/summary/route.js.map +7 -0
  6. package/dist/modules/billing/api/accounts/route.js +46 -4
  7. package/dist/modules/billing/api/accounts/route.js.map +2 -2
  8. package/dist/modules/billing/api/export/account/[id]/route.js +11 -7
  9. package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
  10. package/dist/modules/billing/api/invoices/[id]/memos/route.js +54 -0
  11. package/dist/modules/billing/api/invoices/[id]/memos/route.js.map +7 -0
  12. package/dist/modules/billing/api/invoices/[id]/pdf/route.js +69 -0
  13. package/dist/modules/billing/api/invoices/[id]/pdf/route.js.map +7 -0
  14. package/dist/modules/billing/api/invoices/[id]/route.js +44 -24
  15. package/dist/modules/billing/api/invoices/[id]/route.js.map +2 -2
  16. package/dist/modules/billing/api/invoices/[id]/send/route.js +107 -0
  17. package/dist/modules/billing/api/invoices/[id]/send/route.js.map +7 -0
  18. package/dist/modules/billing/api/invoices/add-line/route.js +2 -2
  19. package/dist/modules/billing/api/invoices/add-line/route.js.map +2 -2
  20. package/dist/modules/billing/api/invoices/delete-draft/route.js +33 -0
  21. package/dist/modules/billing/api/invoices/delete-draft/route.js.map +7 -0
  22. package/dist/modules/billing/api/invoices/edit-line/route.js +2 -2
  23. package/dist/modules/billing/api/invoices/edit-line/route.js.map +2 -2
  24. package/dist/modules/billing/api/invoices/post/route.js +2 -2
  25. package/dist/modules/billing/api/invoices/post/route.js.map +2 -2
  26. package/dist/modules/billing/api/invoices/remove-line/route.js +2 -2
  27. package/dist/modules/billing/api/invoices/remove-line/route.js.map +2 -2
  28. package/dist/modules/billing/api/invoices/route.js +45 -46
  29. package/dist/modules/billing/api/invoices/route.js.map +2 -2
  30. package/dist/modules/billing/api/items/bulk/route.js +1 -1
  31. package/dist/modules/billing/api/items/bulk/route.js.map +2 -2
  32. package/dist/modules/billing/api/items/change-price/route.js +44 -0
  33. package/dist/modules/billing/api/items/change-price/route.js.map +7 -0
  34. package/dist/modules/billing/api/items/route.js +19 -7
  35. package/dist/modules/billing/api/items/route.js.map +2 -2
  36. package/dist/modules/billing/api/payment-imports/[id]/route.js +49 -0
  37. package/dist/modules/billing/api/payment-imports/[id]/route.js.map +7 -0
  38. package/dist/modules/billing/api/payment-imports/route.js +57 -0
  39. package/dist/modules/billing/api/payment-imports/route.js.map +7 -0
  40. package/dist/modules/billing/api/payments/[id]/route.js +60 -0
  41. package/dist/modules/billing/api/payments/[id]/route.js.map +7 -0
  42. package/dist/modules/billing/api/payments/allocate/route.js +35 -0
  43. package/dist/modules/billing/api/payments/allocate/route.js.map +7 -0
  44. package/dist/modules/billing/api/payments/assign/route.js +36 -0
  45. package/dist/modules/billing/api/payments/assign/route.js.map +7 -0
  46. package/dist/modules/billing/api/payments/convert-to-topup/route.js +35 -0
  47. package/dist/modules/billing/api/payments/convert-to-topup/route.js.map +7 -0
  48. package/dist/modules/billing/api/payments/ignore/route.js +35 -0
  49. package/dist/modules/billing/api/payments/ignore/route.js.map +7 -0
  50. package/dist/modules/billing/api/payments/import/route.js +63 -0
  51. package/dist/modules/billing/api/payments/import/route.js.map +7 -0
  52. package/dist/modules/billing/api/payments/restore/route.js +35 -0
  53. package/dist/modules/billing/api/payments/restore/route.js.map +7 -0
  54. package/dist/modules/billing/api/payments/route.js +114 -0
  55. package/dist/modules/billing/api/payments/route.js.map +7 -0
  56. package/dist/modules/billing/api/payments/unallocate/route.js +35 -0
  57. package/dist/modules/billing/api/payments/unallocate/route.js.map +7 -0
  58. package/dist/modules/billing/api/payments/unassign/route.js +35 -0
  59. package/dist/modules/billing/api/payments/unassign/route.js.map +7 -0
  60. package/dist/modules/billing/api/run-outcomes/route.js +43 -6
  61. package/dist/modules/billing/api/run-outcomes/route.js.map +2 -2
  62. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js +61 -0
  63. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js.map +7 -0
  64. package/dist/modules/billing/api/runs/[id]/route.js +53 -0
  65. package/dist/modules/billing/api/runs/[id]/route.js.map +7 -0
  66. package/dist/modules/billing/api/runs/retry-failed/route.js +1 -1
  67. package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
  68. package/dist/modules/billing/api/runs/route.js +9 -3
  69. package/dist/modules/billing/api/runs/route.js.map +2 -2
  70. package/dist/modules/billing/api/settings/number-preview/route.js +42 -0
  71. package/dist/modules/billing/api/settings/number-preview/route.js.map +7 -0
  72. package/dist/modules/billing/api/settings/route.js +92 -0
  73. package/dist/modules/billing/api/settings/route.js.map +7 -0
  74. package/dist/modules/billing/api/statements/route.js +3 -0
  75. package/dist/modules/billing/api/statements/route.js.map +2 -2
  76. package/dist/modules/billing/api/test-invoices/wipe/route.js +2 -2
  77. package/dist/modules/billing/api/test-invoices/wipe/route.js.map +2 -2
  78. package/dist/modules/billing/backend/billing/accounts/[id]/page.js +121 -232
  79. package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
  80. package/dist/modules/billing/backend/billing/accounts/create/page.js +6 -0
  81. package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
  82. package/dist/modules/billing/backend/billing/accounts/page.js +63 -4
  83. package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
  84. package/dist/modules/billing/backend/billing/invoices/[id]/page.js +297 -65
  85. package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +3 -3
  86. package/dist/modules/billing/backend/billing/invoices/page.js +6 -172
  87. package/dist/modules/billing/backend/billing/invoices/page.js.map +2 -2
  88. package/dist/modules/billing/backend/billing/items/[id]/page.js +51 -7
  89. package/dist/modules/billing/backend/billing/items/[id]/page.js.map +2 -2
  90. package/dist/modules/billing/backend/billing/items/create/page.js +9 -6
  91. package/dist/modules/billing/backend/billing/items/create/page.js.map +2 -2
  92. package/dist/modules/billing/backend/billing/items/page.js +8 -2
  93. package/dist/modules/billing/backend/billing/items/page.js.map +2 -2
  94. package/dist/modules/billing/backend/billing/payments/[id]/page.js +297 -0
  95. package/dist/modules/billing/backend/billing/payments/[id]/page.js.map +7 -0
  96. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js +21 -0
  97. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js.map +7 -0
  98. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js +74 -0
  99. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js.map +7 -0
  100. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js +21 -0
  101. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js.map +7 -0
  102. package/dist/modules/billing/backend/billing/payments/page.js +58 -0
  103. package/dist/modules/billing/backend/billing/payments/page.js.map +7 -0
  104. package/dist/modules/billing/backend/billing/payments/page.meta.js +18 -0
  105. package/dist/modules/billing/backend/billing/payments/page.meta.js.map +7 -0
  106. package/dist/modules/billing/backend/billing/runs/[id]/page.js +49 -14
  107. package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +2 -2
  108. package/dist/modules/billing/backend/billing/runs/page.js +58 -7
  109. package/dist/modules/billing/backend/billing/runs/page.js.map +2 -2
  110. package/dist/modules/billing/backend/billing/statements/[id]/page.js +61 -6
  111. package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +2 -2
  112. package/dist/modules/billing/backend/billing/statements/page.js +1 -1
  113. package/dist/modules/billing/backend/billing/statements/page.js.map +2 -2
  114. package/dist/modules/billing/backend/billing/topups/page.js +1 -1
  115. package/dist/modules/billing/backend/billing/topups/page.js.map +2 -2
  116. package/dist/modules/billing/backend/billing/transactions/page.js +1 -1
  117. package/dist/modules/billing/backend/billing/transactions/page.js.map +2 -2
  118. package/dist/modules/billing/backend/billing/usage/create/page.js +3 -2
  119. package/dist/modules/billing/backend/billing/usage/create/page.js.map +2 -2
  120. package/dist/modules/billing/backend/config/billing/page.js +472 -0
  121. package/dist/modules/billing/backend/config/billing/page.js.map +7 -0
  122. package/dist/modules/billing/backend/config/billing/page.meta.js +19 -0
  123. package/dist/modules/billing/backend/config/billing/page.meta.js.map +7 -0
  124. package/dist/modules/billing/cli.js +22 -1
  125. package/dist/modules/billing/cli.js.map +2 -2
  126. package/dist/modules/billing/commands/accounts.js +6 -0
  127. package/dist/modules/billing/commands/accounts.js.map +2 -2
  128. package/dist/modules/billing/commands/index.js +3 -0
  129. package/dist/modules/billing/commands/index.js.map +2 -2
  130. package/dist/modules/billing/commands/invoices.js +269 -167
  131. package/dist/modules/billing/commands/invoices.js.map +2 -2
  132. package/dist/modules/billing/commands/items.js +108 -0
  133. package/dist/modules/billing/commands/items.js.map +2 -2
  134. package/dist/modules/billing/commands/memos.js +179 -0
  135. package/dist/modules/billing/commands/memos.js.map +7 -0
  136. package/dist/modules/billing/commands/payments.js +379 -0
  137. package/dist/modules/billing/commands/payments.js.map +7 -0
  138. package/dist/modules/billing/commands/runs.js +151 -4
  139. package/dist/modules/billing/commands/runs.js.map +2 -2
  140. package/dist/modules/billing/commands/settings.js +59 -0
  141. package/dist/modules/billing/commands/settings.js.map +7 -0
  142. package/dist/modules/billing/commands/topups.js +9 -2
  143. package/dist/modules/billing/commands/topups.js.map +2 -2
  144. package/dist/modules/billing/commands/transactions.js +2 -2
  145. package/dist/modules/billing/commands/transactions.js.map +2 -2
  146. package/dist/modules/billing/components/AccountForm.js +149 -42
  147. package/dist/modules/billing/components/AccountForm.js.map +2 -2
  148. package/dist/modules/billing/components/AccountInvoicesSection.js +64 -0
  149. package/dist/modules/billing/components/AccountInvoicesSection.js.map +7 -0
  150. package/dist/modules/billing/components/AccountPaymentsSection.js +43 -0
  151. package/dist/modules/billing/components/AccountPaymentsSection.js.map +7 -0
  152. package/dist/modules/billing/components/AccountPicker.js +56 -0
  153. package/dist/modules/billing/components/AccountPicker.js.map +7 -0
  154. package/dist/modules/billing/components/AllocatePaymentDialog.js +134 -0
  155. package/dist/modules/billing/components/AllocatePaymentDialog.js.map +7 -0
  156. package/dist/modules/billing/components/BillingDataTable.js +21 -0
  157. package/dist/modules/billing/components/BillingDataTable.js.map +7 -0
  158. package/dist/modules/billing/components/BillingSettingsLink.js +19 -0
  159. package/dist/modules/billing/components/BillingSettingsLink.js.map +7 -0
  160. package/dist/modules/billing/components/ChangeItemPriceDialog.js +158 -0
  161. package/dist/modules/billing/components/ChangeItemPriceDialog.js.map +7 -0
  162. package/dist/modules/billing/components/CreateMemoDialog.js +200 -0
  163. package/dist/modules/billing/components/CreateMemoDialog.js.map +7 -0
  164. package/dist/modules/billing/components/CustomerLabel.js +33 -0
  165. package/dist/modules/billing/components/CustomerLabel.js.map +7 -0
  166. package/dist/modules/billing/components/InvoicesTable.js +270 -0
  167. package/dist/modules/billing/components/InvoicesTable.js.map +7 -0
  168. package/dist/modules/billing/components/ItemForm.js +176 -44
  169. package/dist/modules/billing/components/ItemForm.js.map +2 -2
  170. package/dist/modules/billing/components/ItemPriceField.js +46 -0
  171. package/dist/modules/billing/components/ItemPriceField.js.map +7 -0
  172. package/dist/modules/billing/components/LineFormDialog.js +25 -2
  173. package/dist/modules/billing/components/LineFormDialog.js.map +2 -2
  174. package/dist/modules/billing/components/ManualPaymentDialog.js +122 -0
  175. package/dist/modules/billing/components/ManualPaymentDialog.js.map +7 -0
  176. package/dist/modules/billing/components/MoneyInput.js +14 -0
  177. package/dist/modules/billing/components/MoneyInput.js.map +7 -0
  178. package/dist/modules/billing/components/PaymentUploadDialog.js +115 -0
  179. package/dist/modules/billing/components/PaymentUploadDialog.js.map +7 -0
  180. package/dist/modules/billing/components/PaymentsTable.js +171 -0
  181. package/dist/modules/billing/components/PaymentsTable.js.map +7 -0
  182. package/dist/modules/billing/components/PostRunDraftsButton.js +105 -0
  183. package/dist/modules/billing/components/PostRunDraftsButton.js.map +7 -0
  184. package/dist/modules/billing/components/PrepaidPanel.js +14 -4
  185. package/dist/modules/billing/components/PrepaidPanel.js.map +2 -2
  186. package/dist/modules/billing/components/RunCyclesSection.js +209 -0
  187. package/dist/modules/billing/components/RunCyclesSection.js.map +7 -0
  188. package/dist/modules/billing/components/TestRunActions.js +127 -0
  189. package/dist/modules/billing/components/TestRunActions.js.map +7 -0
  190. package/dist/modules/billing/components/account/AccountItemsTab.js +157 -0
  191. package/dist/modules/billing/components/account/AccountItemsTab.js.map +7 -0
  192. package/dist/modules/billing/components/account/AccountRunBillingDialog.js +122 -0
  193. package/dist/modules/billing/components/account/AccountRunBillingDialog.js.map +7 -0
  194. package/dist/modules/billing/components/account/AccountRunsTab.js +152 -0
  195. package/dist/modules/billing/components/account/AccountRunsTab.js.map +7 -0
  196. package/dist/modules/billing/components/account/AccountStatementsTab.js +98 -0
  197. package/dist/modules/billing/components/account/AccountStatementsTab.js.map +7 -0
  198. package/dist/modules/billing/components/account/AccountSummaryCards.js +125 -0
  199. package/dist/modules/billing/components/account/AccountSummaryCards.js.map +7 -0
  200. package/dist/modules/billing/components/account/AccountUsageTab.js +91 -0
  201. package/dist/modules/billing/components/account/AccountUsageTab.js.map +7 -0
  202. package/dist/modules/billing/components/account/AccountView.js +73 -0
  203. package/dist/modules/billing/components/account/AccountView.js.map +7 -0
  204. package/dist/modules/billing/components/account/accountRow.js +39 -0
  205. package/dist/modules/billing/components/account/accountRow.js.map +7 -0
  206. package/dist/modules/billing/components/customerNames.js +42 -0
  207. package/dist/modules/billing/components/customerNames.js.map +7 -0
  208. package/dist/modules/billing/data/entities.js +560 -4
  209. package/dist/modules/billing/data/entities.js.map +2 -2
  210. package/dist/modules/billing/data/entityIds.js +10 -1
  211. package/dist/modules/billing/data/entityIds.js.map +2 -2
  212. package/dist/modules/billing/data/validators.js +269 -15
  213. package/dist/modules/billing/data/validators.js.map +2 -2
  214. package/dist/modules/billing/events.js +50 -0
  215. package/dist/modules/billing/events.js.map +2 -2
  216. package/dist/modules/billing/i18n/en.json +457 -21
  217. package/dist/modules/billing/i18n/pl.json +457 -21
  218. package/dist/modules/billing/index.js +6 -2
  219. package/dist/modules/billing/index.js.map +2 -2
  220. package/dist/modules/billing/lib/accountFinancials.js +58 -0
  221. package/dist/modules/billing/lib/accountFinancials.js.map +7 -0
  222. package/dist/modules/billing/lib/accountLock.js +14 -0
  223. package/dist/modules/billing/lib/accountLock.js.map +7 -0
  224. package/dist/modules/billing/lib/accountStatus.js +9 -7
  225. package/dist/modules/billing/lib/accountStatus.js.map +2 -2
  226. package/dist/modules/billing/lib/accountSummary.js +106 -0
  227. package/dist/modules/billing/lib/accountSummary.js.map +7 -0
  228. package/dist/modules/billing/lib/allocation.js +356 -0
  229. package/dist/modules/billing/lib/allocation.js.map +7 -0
  230. package/dist/modules/billing/lib/allocationEvents.js +22 -0
  231. package/dist/modules/billing/lib/allocationEvents.js.map +7 -0
  232. package/dist/modules/billing/lib/billPeriod.js +15 -1
  233. package/dist/modules/billing/lib/billPeriod.js.map +2 -2
  234. package/dist/modules/billing/lib/billRunEngine.js +237 -102
  235. package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
  236. package/dist/modules/billing/lib/billingSettings.js +116 -0
  237. package/dist/modules/billing/lib/billingSettings.js.map +7 -0
  238. package/dist/modules/billing/lib/commandRoute.js +81 -0
  239. package/dist/modules/billing/lib/commandRoute.js.map +7 -0
  240. package/dist/modules/billing/lib/customerLookup.js +97 -0
  241. package/dist/modules/billing/lib/customerLookup.js.map +7 -0
  242. package/dist/modules/billing/lib/dateOnly.js +13 -0
  243. package/dist/modules/billing/lib/dateOnly.js.map +7 -0
  244. package/dist/modules/billing/lib/documentParties.js +16 -0
  245. package/dist/modules/billing/lib/documentParties.js.map +7 -0
  246. package/dist/modules/billing/lib/documentTypes.js +27 -0
  247. package/dist/modules/billing/lib/documentTypes.js.map +7 -0
  248. package/dist/modules/billing/lib/dueDate.js +18 -0
  249. package/dist/modules/billing/lib/dueDate.js.map +7 -0
  250. package/dist/modules/billing/lib/iban.js +29 -0
  251. package/dist/modules/billing/lib/iban.js.map +7 -0
  252. package/dist/modules/billing/lib/invoiceDelivery.js +131 -0
  253. package/dist/modules/billing/lib/invoiceDelivery.js.map +7 -0
  254. package/dist/modules/billing/lib/invoiceLanguages.js +16 -0
  255. package/dist/modules/billing/lib/invoiceLanguages.js.map +7 -0
  256. package/dist/modules/billing/lib/invoiceWriter.js +107 -99
  257. package/dist/modules/billing/lib/invoiceWriter.js.map +2 -2
  258. package/dist/modules/billing/lib/itemBillingState.js +27 -0
  259. package/dist/modules/billing/lib/itemBillingState.js.map +7 -0
  260. package/dist/modules/billing/lib/itemPriceChange.js +18 -0
  261. package/dist/modules/billing/lib/itemPriceChange.js.map +7 -0
  262. package/dist/modules/billing/lib/itemSelector.js.map +2 -2
  263. package/dist/modules/billing/lib/lineAmounts.js +32 -0
  264. package/dist/modules/billing/lib/lineAmounts.js.map +7 -0
  265. package/dist/modules/billing/lib/memoGuards.js +25 -0
  266. package/dist/modules/billing/lib/memoGuards.js.map +7 -0
  267. package/dist/modules/billing/lib/navigation.js +48 -0
  268. package/dist/modules/billing/lib/navigation.js.map +7 -0
  269. package/dist/modules/billing/lib/numbering.js +120 -0
  270. package/dist/modules/billing/lib/numbering.js.map +7 -0
  271. package/dist/modules/billing/lib/overdue.js +36 -0
  272. package/dist/modules/billing/lib/overdue.js.map +7 -0
  273. package/dist/modules/billing/lib/paymentCsv.js +125 -0
  274. package/dist/modules/billing/lib/paymentCsv.js.map +7 -0
  275. package/dist/modules/billing/lib/paymentGateway.js +15 -0
  276. package/dist/modules/billing/lib/paymentGateway.js.map +7 -0
  277. package/dist/modules/billing/lib/paymentImport.js +164 -0
  278. package/dist/modules/billing/lib/paymentImport.js.map +7 -0
  279. package/dist/modules/billing/lib/paymentMatcher.js +36 -0
  280. package/dist/modules/billing/lib/paymentMatcher.js.map +7 -0
  281. package/dist/modules/billing/lib/pdf/attachmentStore.js +46 -0
  282. package/dist/modules/billing/lib/pdf/attachmentStore.js.map +7 -0
  283. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js +7 -0
  284. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js.map +7 -0
  285. package/dist/modules/billing/lib/pdf/invoiceModel.js +194 -0
  286. package/dist/modules/billing/lib/pdf/invoiceModel.js.map +7 -0
  287. package/dist/modules/billing/lib/pdf/invoicePdf.js +165 -0
  288. package/dist/modules/billing/lib/pdf/invoicePdf.js.map +7 -0
  289. package/dist/modules/billing/lib/pdf/invoiceTemplate.js +274 -0
  290. package/dist/modules/billing/lib/pdf/invoiceTemplate.js.map +7 -0
  291. package/dist/modules/billing/lib/prepaidConfig.js +17 -25
  292. package/dist/modules/billing/lib/prepaidConfig.js.map +2 -2
  293. package/dist/modules/billing/lib/prepaidConsume.js +26 -7
  294. package/dist/modules/billing/lib/prepaidConsume.js.map +2 -2
  295. package/dist/modules/billing/lib/prepaidPeriodClose.js +277 -158
  296. package/dist/modules/billing/lib/prepaidPeriodClose.js.map +2 -2
  297. package/dist/modules/billing/lib/prepaidTaxPoint.js +37 -0
  298. package/dist/modules/billing/lib/prepaidTaxPoint.js.map +7 -0
  299. package/dist/modules/billing/lib/seeds/demo.js +51 -3
  300. package/dist/modules/billing/lib/seeds/demo.js.map +2 -2
  301. package/dist/modules/billing/lib/seeds/paymentsSample.js +93 -0
  302. package/dist/modules/billing/lib/seeds/paymentsSample.js.map +7 -0
  303. package/dist/modules/billing/lib/seeds/sellerSettings.js +41 -0
  304. package/dist/modules/billing/lib/seeds/sellerSettings.js.map +7 -0
  305. package/dist/modules/billing/lib/servicePeriods.js +121 -0
  306. package/dist/modules/billing/lib/servicePeriods.js.map +7 -0
  307. package/dist/modules/billing/lib/testRunCleanup.js +24 -0
  308. package/dist/modules/billing/lib/testRunCleanup.js.map +7 -0
  309. package/dist/modules/billing/lib/topupCapture.js +2 -2
  310. package/dist/modules/billing/lib/topupCapture.js.map +2 -2
  311. package/dist/modules/billing/lib/topupReceipt.js +58 -79
  312. package/dist/modules/billing/lib/topupReceipt.js.map +2 -2
  313. package/dist/modules/billing/lib/usageRater.js.map +1 -1
  314. package/dist/modules/billing/lib/usageRunner.js +14 -6
  315. package/dist/modules/billing/lib/usageRunner.js.map +2 -2
  316. package/dist/modules/billing/lib/vat.js +39 -0
  317. package/dist/modules/billing/lib/vat.js.map +7 -0
  318. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js +15 -0
  319. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js.map +7 -0
  320. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js +25 -0
  321. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js.map +7 -0
  322. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js +115 -0
  323. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js.map +7 -0
  324. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js +47 -0
  325. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js.map +7 -0
  326. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js +89 -0
  327. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js.map +7 -0
  328. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js +17 -0
  329. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js.map +7 -0
  330. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js +27 -0
  331. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js.map +7 -0
  332. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js +29 -0
  333. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js.map +7 -0
  334. package/dist/modules/billing/setup.js +7 -184
  335. package/dist/modules/billing/setup.js.map +2 -2
  336. package/dist/modules/billing/subscribers/credit-memo-auto-send.js +16 -0
  337. package/dist/modules/billing/subscribers/credit-memo-auto-send.js.map +7 -0
  338. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js +16 -0
  339. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js.map +7 -0
  340. package/dist/modules/billing/subscribers/debit-memo-auto-send.js +16 -0
  341. package/dist/modules/billing/subscribers/debit-memo-auto-send.js.map +7 -0
  342. package/dist/modules/billing/subscribers/invoice-apply-credit.js +34 -0
  343. package/dist/modules/billing/subscribers/invoice-apply-credit.js.map +7 -0
  344. package/dist/modules/billing/subscribers/invoice-auto-send.js +30 -0
  345. package/dist/modules/billing/subscribers/invoice-auto-send.js.map +7 -0
  346. package/dist/modules/billing/subscribers/payment-captured-to-paid.js +69 -33
  347. package/dist/modules/billing/subscribers/payment-captured-to-paid.js.map +2 -2
  348. package/dist/modules/billing/subscribers/prepaid-topup-captured.js +4 -6
  349. package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +2 -2
  350. package/generated/entities/billing_account/index.ts +3 -0
  351. package/generated/entities/billing_allocation/index.ts +18 -0
  352. package/generated/entities/billing_document_sequence/index.ts +11 -0
  353. package/generated/entities/billing_invoice/index.ts +38 -0
  354. package/generated/entities/billing_invoice_line/index.ts +24 -0
  355. package/generated/entities/billing_item/index.ts +4 -0
  356. package/generated/entities/billing_payment/index.ts +25 -0
  357. package/generated/entities/billing_payment_import/index.ts +14 -0
  358. package/generated/entities/billing_run/index.ts +0 -1
  359. package/generated/entities/billing_run_outcome/index.ts +1 -0
  360. package/generated/entities/billing_settings/index.ts +26 -0
  361. package/generated/entities/billing_statement/index.ts +1 -0
  362. package/generated/entities.ids.generated.ts +7 -0
  363. package/package.json +5 -1
  364. package/src/modules/billing/__integration__/TC-BILL-004.spec.ts +170 -0
  365. package/src/modules/billing/__integration__/TC-BILL-005.spec.ts +134 -0
  366. package/src/modules/billing/__integration__/TC-BILL-006.spec.ts +101 -0
  367. package/src/modules/billing/__integration__/TC-BILL-007.spec.ts +140 -0
  368. package/src/modules/billing/__integration__/TC-BILL-008.spec.ts +108 -0
  369. package/src/modules/billing/__integration__/TC-BILL-009.spec.ts +113 -0
  370. package/src/modules/billing/__integration__/TC-BILL-010.spec.ts +118 -0
  371. package/src/modules/billing/__integration__/TC-BILL-011.spec.ts +142 -0
  372. package/src/modules/billing/__integration__/TC-BILL-012.spec.ts +115 -0
  373. package/src/modules/billing/__integration__/TC-BILL-013.spec.ts +118 -0
  374. package/src/modules/billing/__integration__/sellerSettings.ts +24 -0
  375. package/src/modules/billing/__tests__/commands/invoices.test.ts +436 -354
  376. package/src/modules/billing/__tests__/commands/itemsBulk.test.ts +33 -0
  377. package/src/modules/billing/__tests__/commands/memos.test.ts +205 -0
  378. package/src/modules/billing/__tests__/commands/payments.test.ts +241 -0
  379. package/src/modules/billing/__tests__/commands/runsDelete.test.ts +146 -0
  380. package/src/modules/billing/__tests__/commands/runsPostDrafts.test.ts +106 -0
  381. package/src/modules/billing/__tests__/commands/topups.test.ts +11 -0
  382. package/src/modules/billing/__tests__/entityRegistry.test.ts +8 -1
  383. package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +28 -11
  384. package/src/modules/billing/__tests__/lib/accountFinancials.test.ts +53 -0
  385. package/src/modules/billing/__tests__/lib/accountSummary.test.ts +97 -0
  386. package/src/modules/billing/__tests__/lib/allocation.test.ts +319 -0
  387. package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +228 -17
  388. package/src/modules/billing/__tests__/lib/billingSettings.test.ts +109 -0
  389. package/src/modules/billing/__tests__/lib/customerLookup.test.ts +156 -0
  390. package/src/modules/billing/__tests__/lib/dueDate.test.ts +35 -0
  391. package/src/modules/billing/__tests__/lib/iban.test.ts +38 -0
  392. package/src/modules/billing/__tests__/lib/invoiceDelivery.test.ts +189 -0
  393. package/src/modules/billing/__tests__/lib/invoiceWriter.test.ts +212 -10
  394. package/src/modules/billing/__tests__/lib/itemBillingState.test.ts +42 -0
  395. package/src/modules/billing/__tests__/lib/itemPriceChange.test.ts +165 -0
  396. package/src/modules/billing/__tests__/lib/lineAmounts.test.ts +86 -0
  397. package/src/modules/billing/__tests__/lib/navigation.test.ts +71 -0
  398. package/src/modules/billing/__tests__/lib/numbering.test.ts +91 -0
  399. package/src/modules/billing/__tests__/lib/overdue.test.ts +36 -0
  400. package/src/modules/billing/__tests__/lib/paymentCsv.test.ts +148 -0
  401. package/src/modules/billing/__tests__/lib/paymentImport.test.ts +113 -0
  402. package/src/modules/billing/__tests__/lib/paymentsSample.test.ts +73 -0
  403. package/src/modules/billing/__tests__/lib/pdf/invoicePdf.test.ts +199 -0
  404. package/src/modules/billing/__tests__/lib/pdf/invoicePdfService.test.ts +147 -0
  405. package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +374 -98
  406. package/src/modules/billing/__tests__/lib/prepaidTaxPoint.test.ts +118 -0
  407. package/src/modules/billing/__tests__/lib/seedsDemo.test.ts +53 -2
  408. package/src/modules/billing/__tests__/lib/sellerSettings.test.ts +28 -0
  409. package/src/modules/billing/__tests__/lib/servicePeriods.test.ts +190 -0
  410. package/src/modules/billing/__tests__/lib/topupCapture.test.ts +6 -28
  411. package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +21 -52
  412. package/src/modules/billing/__tests__/lib/vat.test.ts +51 -0
  413. package/src/modules/billing/__tests__/package-isolation.test.ts +22 -8
  414. package/src/modules/billing/__tests__/setup.test.ts +13 -4
  415. package/src/modules/billing/__tests__/validators.test.ts +193 -3
  416. package/src/modules/billing/acl.ts +12 -0
  417. package/src/modules/billing/api/accounts/[id]/summary/route.ts +62 -0
  418. package/src/modules/billing/api/accounts/route.ts +58 -4
  419. package/src/modules/billing/api/export/account/[id]/route.ts +13 -11
  420. package/src/modules/billing/api/invoices/[id]/memos/route.ts +63 -0
  421. package/src/modules/billing/api/invoices/[id]/pdf/route.ts +79 -0
  422. package/src/modules/billing/api/invoices/[id]/route.ts +57 -70
  423. package/src/modules/billing/api/invoices/[id]/send/route.ts +127 -0
  424. package/src/modules/billing/api/invoices/add-line/route.ts +2 -2
  425. package/src/modules/billing/api/invoices/delete-draft/route.ts +33 -0
  426. package/src/modules/billing/api/invoices/edit-line/route.ts +2 -2
  427. package/src/modules/billing/api/invoices/post/route.ts +7 -6
  428. package/src/modules/billing/api/invoices/remove-line/route.ts +2 -2
  429. package/src/modules/billing/api/invoices/route.ts +81 -86
  430. package/src/modules/billing/api/items/bulk/route.ts +2 -1
  431. package/src/modules/billing/api/items/change-price/route.ts +54 -0
  432. package/src/modules/billing/api/items/route.ts +23 -7
  433. package/src/modules/billing/api/payment-imports/[id]/route.ts +49 -0
  434. package/src/modules/billing/api/payment-imports/route.ts +57 -0
  435. package/src/modules/billing/api/payments/[id]/route.ts +63 -0
  436. package/src/modules/billing/api/payments/allocate/route.ts +35 -0
  437. package/src/modules/billing/api/payments/assign/route.ts +36 -0
  438. package/src/modules/billing/api/payments/convert-to-topup/route.ts +35 -0
  439. package/src/modules/billing/api/payments/ignore/route.ts +35 -0
  440. package/src/modules/billing/api/payments/import/route.ts +72 -0
  441. package/src/modules/billing/api/payments/restore/route.ts +35 -0
  442. package/src/modules/billing/api/payments/route.ts +147 -0
  443. package/src/modules/billing/api/payments/unallocate/route.ts +35 -0
  444. package/src/modules/billing/api/payments/unassign/route.ts +35 -0
  445. package/src/modules/billing/api/run-outcomes/route.ts +49 -6
  446. package/src/modules/billing/api/runs/[id]/post-drafts/route.ts +71 -0
  447. package/src/modules/billing/api/runs/[id]/route.ts +59 -0
  448. package/src/modules/billing/api/runs/retry-failed/route.ts +2 -1
  449. package/src/modules/billing/api/runs/route.ts +15 -7
  450. package/src/modules/billing/api/settings/number-preview/route.ts +46 -0
  451. package/src/modules/billing/api/settings/route.ts +116 -0
  452. package/src/modules/billing/api/statements/route.ts +5 -0
  453. package/src/modules/billing/api/test-invoices/wipe/route.ts +2 -2
  454. package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +151 -372
  455. package/src/modules/billing/backend/billing/accounts/create/page.tsx +6 -0
  456. package/src/modules/billing/backend/billing/accounts/page.tsx +70 -4
  457. package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +444 -95
  458. package/src/modules/billing/backend/billing/invoices/page.tsx +8 -227
  459. package/src/modules/billing/backend/billing/items/[id]/page.tsx +75 -6
  460. package/src/modules/billing/backend/billing/items/create/page.tsx +13 -8
  461. package/src/modules/billing/backend/billing/items/page.tsx +18 -2
  462. package/src/modules/billing/backend/billing/payments/[id]/page.meta.ts +19 -0
  463. package/src/modules/billing/backend/billing/payments/[id]/page.tsx +415 -0
  464. package/src/modules/billing/backend/billing/payments/imports/[id]/page.meta.ts +19 -0
  465. package/src/modules/billing/backend/billing/payments/imports/[id]/page.tsx +106 -0
  466. package/src/modules/billing/backend/billing/payments/page.meta.ts +16 -0
  467. package/src/modules/billing/backend/billing/payments/page.tsx +56 -0
  468. package/src/modules/billing/backend/billing/runs/[id]/page.tsx +49 -18
  469. package/src/modules/billing/backend/billing/runs/page.tsx +59 -8
  470. package/src/modules/billing/backend/billing/statements/[id]/page.tsx +86 -5
  471. package/src/modules/billing/backend/billing/statements/page.tsx +1 -1
  472. package/src/modules/billing/backend/billing/topups/page.tsx +1 -1
  473. package/src/modules/billing/backend/billing/transactions/page.tsx +1 -1
  474. package/src/modules/billing/backend/billing/usage/create/page.tsx +3 -2
  475. package/src/modules/billing/backend/config/billing/page.meta.ts +21 -0
  476. package/src/modules/billing/backend/config/billing/page.tsx +549 -0
  477. package/src/modules/billing/cli.ts +31 -1
  478. package/src/modules/billing/commands/accounts.ts +6 -0
  479. package/src/modules/billing/commands/index.ts +3 -0
  480. package/src/modules/billing/commands/invoices.ts +364 -231
  481. package/src/modules/billing/commands/items.ts +143 -0
  482. package/src/modules/billing/commands/memos.ts +218 -0
  483. package/src/modules/billing/commands/payments.ts +428 -0
  484. package/src/modules/billing/commands/runs.ts +205 -3
  485. package/src/modules/billing/commands/settings.ts +77 -0
  486. package/src/modules/billing/commands/topups.ts +12 -3
  487. package/src/modules/billing/commands/transactions.ts +2 -2
  488. package/src/modules/billing/components/AccountForm.tsx +199 -53
  489. package/src/modules/billing/components/AccountInvoicesSection.tsx +75 -0
  490. package/src/modules/billing/components/AccountPaymentsSection.tsx +42 -0
  491. package/src/modules/billing/components/AccountPicker.tsx +70 -0
  492. package/src/modules/billing/components/AllocatePaymentDialog.tsx +167 -0
  493. package/src/modules/billing/components/BillingDataTable.tsx +34 -0
  494. package/src/modules/billing/components/BillingSettingsLink.tsx +25 -0
  495. package/src/modules/billing/components/ChangeItemPriceDialog.tsx +180 -0
  496. package/src/modules/billing/components/CreateMemoDialog.tsx +232 -0
  497. package/src/modules/billing/components/CustomerLabel.tsx +53 -0
  498. package/src/modules/billing/components/InvoicesTable.tsx +344 -0
  499. package/src/modules/billing/components/ItemForm.tsx +233 -44
  500. package/src/modules/billing/components/ItemPriceField.tsx +81 -0
  501. package/src/modules/billing/components/LineFormDialog.tsx +25 -1
  502. package/src/modules/billing/components/ManualPaymentDialog.tsx +140 -0
  503. package/src/modules/billing/components/MoneyInput.tsx +26 -0
  504. package/src/modules/billing/components/PaymentUploadDialog.tsx +148 -0
  505. package/src/modules/billing/components/PaymentsTable.tsx +213 -0
  506. package/src/modules/billing/components/PostRunDraftsButton.tsx +129 -0
  507. package/src/modules/billing/components/PrepaidPanel.tsx +19 -6
  508. package/src/modules/billing/components/RunCyclesSection.tsx +298 -0
  509. package/src/modules/billing/components/TestRunActions.tsx +152 -0
  510. package/src/modules/billing/components/account/AccountItemsTab.tsx +192 -0
  511. package/src/modules/billing/components/account/AccountRunBillingDialog.tsx +147 -0
  512. package/src/modules/billing/components/account/AccountRunsTab.tsx +201 -0
  513. package/src/modules/billing/components/account/AccountStatementsTab.tsx +120 -0
  514. package/src/modules/billing/components/account/AccountSummaryCards.tsx +153 -0
  515. package/src/modules/billing/components/account/AccountUsageTab.tsx +122 -0
  516. package/src/modules/billing/components/account/AccountView.tsx +101 -0
  517. package/src/modules/billing/components/account/accountRow.ts +70 -0
  518. package/src/modules/billing/components/customerNames.ts +62 -0
  519. package/src/modules/billing/data/entities.ts +654 -5
  520. package/src/modules/billing/data/entityIds.ts +9 -0
  521. package/src/modules/billing/data/validators.ts +388 -8
  522. package/src/modules/billing/events.ts +56 -3
  523. package/src/modules/billing/i18n/en.json +457 -21
  524. package/src/modules/billing/i18n/pl.json +457 -21
  525. package/src/modules/billing/index.ts +6 -2
  526. package/src/modules/billing/lib/accountFinancials.ts +84 -0
  527. package/src/modules/billing/lib/accountLock.ts +32 -0
  528. package/src/modules/billing/lib/accountStatus.ts +11 -9
  529. package/src/modules/billing/lib/accountSummary.ts +172 -0
  530. package/src/modules/billing/lib/allocation.ts +515 -0
  531. package/src/modules/billing/lib/allocationEvents.ts +26 -0
  532. package/src/modules/billing/lib/billPeriod.ts +19 -0
  533. package/src/modules/billing/lib/billRunEngine.ts +350 -150
  534. package/src/modules/billing/lib/billingSettings.ts +145 -0
  535. package/src/modules/billing/lib/commandRoute.ts +112 -0
  536. package/src/modules/billing/lib/customerLookup.ts +157 -0
  537. package/src/modules/billing/lib/dateOnly.ts +22 -0
  538. package/src/modules/billing/lib/documentParties.ts +23 -0
  539. package/src/modules/billing/lib/documentTypes.ts +37 -0
  540. package/src/modules/billing/lib/dueDate.ts +26 -0
  541. package/src/modules/billing/lib/iban.ts +36 -0
  542. package/src/modules/billing/lib/invoiceDelivery.ts +197 -0
  543. package/src/modules/billing/lib/invoiceLanguages.ts +34 -0
  544. package/src/modules/billing/lib/invoiceWriter.ts +162 -148
  545. package/src/modules/billing/lib/itemBillingState.ts +67 -0
  546. package/src/modules/billing/lib/itemPriceChange.ts +56 -0
  547. package/src/modules/billing/lib/itemSelector.ts +4 -1
  548. package/src/modules/billing/lib/lineAmounts.ts +61 -0
  549. package/src/modules/billing/lib/memoGuards.ts +41 -0
  550. package/src/modules/billing/lib/navigation.ts +64 -0
  551. package/src/modules/billing/lib/numbering.ts +174 -0
  552. package/src/modules/billing/lib/overdue.ts +60 -0
  553. package/src/modules/billing/lib/paymentCsv.ts +171 -0
  554. package/src/modules/billing/lib/paymentGateway.ts +52 -0
  555. package/src/modules/billing/lib/paymentImport.ts +228 -0
  556. package/src/modules/billing/lib/paymentMatcher.ts +88 -0
  557. package/src/modules/billing/lib/pdf/attachmentStore.ts +60 -0
  558. package/src/modules/billing/lib/pdf/fonts/NotoSans-Bold.ttf +0 -0
  559. package/src/modules/billing/lib/pdf/fonts/NotoSans-Regular.ttf +0 -0
  560. package/src/modules/billing/lib/pdf/fonts/OFL.txt +93 -0
  561. package/src/modules/billing/lib/pdf/fonts/embeddedFonts.ts +9 -0
  562. package/src/modules/billing/lib/pdf/invoiceModel.ts +328 -0
  563. package/src/modules/billing/lib/pdf/invoicePdf.ts +216 -0
  564. package/src/modules/billing/lib/pdf/invoiceTemplate.ts +338 -0
  565. package/src/modules/billing/lib/prepaidConfig.ts +27 -36
  566. package/src/modules/billing/lib/prepaidConsume.ts +29 -6
  567. package/src/modules/billing/lib/prepaidPeriodClose.ts +407 -239
  568. package/src/modules/billing/lib/prepaidTaxPoint.ts +76 -0
  569. package/src/modules/billing/lib/seeds/demo.ts +79 -6
  570. package/src/modules/billing/lib/seeds/paymentsSample.ts +136 -0
  571. package/src/modules/billing/lib/seeds/sellerSettings.ts +78 -0
  572. package/src/modules/billing/lib/servicePeriods.ts +226 -0
  573. package/src/modules/billing/lib/testRunCleanup.ts +42 -0
  574. package/src/modules/billing/lib/topupCapture.ts +3 -4
  575. package/src/modules/billing/lib/topupReceipt.ts +70 -103
  576. package/src/modules/billing/lib/usageRater.ts +1 -1
  577. package/src/modules/billing/lib/usageRunner.ts +17 -5
  578. package/src/modules/billing/lib/vat.ts +56 -0
  579. package/src/modules/billing/migrations/Migration20261114000000_billing_customer_kind.ts +25 -0
  580. package/src/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.ts +37 -0
  581. package/src/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.ts +132 -0
  582. package/src/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.ts +58 -0
  583. package/src/modules/billing/migrations/Migration20261117000000_billing_payments.ts +97 -0
  584. package/src/modules/billing/migrations/Migration20261118000000_billing_memos.ts +21 -0
  585. package/src/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.ts +39 -0
  586. package/src/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.ts +35 -0
  587. package/src/modules/billing/setup.ts +11 -237
  588. package/src/modules/billing/subscribers/credit-memo-auto-send.ts +15 -0
  589. package/src/modules/billing/subscribers/debit-memo-apply-credit.ts +15 -0
  590. package/src/modules/billing/subscribers/debit-memo-auto-send.ts +15 -0
  591. package/src/modules/billing/subscribers/invoice-apply-credit.ts +39 -0
  592. package/src/modules/billing/subscribers/invoice-auto-send.ts +46 -0
  593. package/src/modules/billing/subscribers/payment-captured-to-paid.ts +84 -62
  594. package/src/modules/billing/subscribers/prepaid-topup-captured.ts +4 -8
  595. package/dist/modules/billing/lib/invoiceStatus.js +0 -38
  596. package/dist/modules/billing/lib/invoiceStatus.js.map +0 -7
  597. package/src/modules/billing/__tests__/lib/invoiceStatus.test.ts +0 -97
  598. package/src/modules/billing/lib/invoiceStatus.ts +0 -61
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Where an item stands against today (account Items tab): has it been billed,
3
+ * and through which date? Pure — computed from the item row and today's date.
4
+ *
5
+ * inactive the item is switched off
6
+ * metered usage item (billed from usage records, no watermark)
7
+ * scheduled starts after today, nothing billed yet
8
+ * awaiting_run started, but no Bill Run has charged it yet
9
+ * billed one-time item already charged
10
+ * current recurring, charged through today or later (paid ahead)
11
+ * due recurring, charged only through a past date — the next
12
+ * Bill Run will charge the rest
13
+ * fully_billed recurring with an end date, charged through that end
14
+ */
15
+
16
+ export type ItemBillingStatus =
17
+ | 'inactive'
18
+ | 'metered'
19
+ | 'scheduled'
20
+ | 'awaiting_run'
21
+ | 'billed'
22
+ | 'current'
23
+ | 'due'
24
+ | 'fully_billed'
25
+
26
+ export type ItemBillingInput = {
27
+ type: 'one_time' | 'recurring' | 'usage'
28
+ is_active: boolean
29
+ bill_start_date: string
30
+ bill_end_date?: string | null
31
+ billed_to_date?: string | null
32
+ }
33
+
34
+ export type ItemBillingState = {
35
+ status: ItemBillingStatus
36
+ /** The relevant date for the label: start, billed-through or due-since day (YYYY-MM-DD). */
37
+ date: string | null
38
+ }
39
+
40
+ function day(value: string | null | undefined): string | null {
41
+ return value ? value.slice(0, 10) : null
42
+ }
43
+
44
+ function nextDay(iso: string): string {
45
+ const date = new Date(`${iso}T00:00:00Z`)
46
+ date.setUTCDate(date.getUTCDate() + 1)
47
+ return date.toISOString().slice(0, 10)
48
+ }
49
+
50
+ /** `today` is a YYYY-MM-DD string (the viewer's calendar day). */
51
+ export function itemBillingState(item: ItemBillingInput, today: string): ItemBillingState {
52
+ if (!item.is_active) return { status: 'inactive', date: null }
53
+ if (item.type === 'usage') return { status: 'metered', date: null }
54
+ const start = day(item.bill_start_date) ?? today
55
+ const billedTo = day(item.billed_to_date)
56
+ const end = day(item.bill_end_date)
57
+
58
+ if (item.type === 'one_time') {
59
+ if (billedTo) return { status: 'billed', date: null }
60
+ return start > today ? { status: 'scheduled', date: start } : { status: 'awaiting_run', date: null }
61
+ }
62
+
63
+ if (!billedTo) return start > today ? { status: 'scheduled', date: start } : { status: 'awaiting_run', date: null }
64
+ if (end && billedTo >= end) return { status: 'fully_billed', date: billedTo }
65
+ if (billedTo >= today) return { status: 'current', date: billedTo }
66
+ return { status: 'due', date: nextDay(billedTo) }
67
+ }
@@ -0,0 +1,56 @@
1
+ import { toIsoDate, toUtcMidnight } from './dateOnly'
2
+
3
+ /**
4
+ * Change the price of a recurring item from a date on (Spec F follow-up):
5
+ * the current item ends the day before `effectiveDate`, and a copy with the
6
+ * new price starts on `effectiveDate` and keeps the original end date.
7
+ *
8
+ * Nothing is billed twice: if the old item was already billed past the
9
+ * change (in advance), the engine credits the unused days (prorate on) and
10
+ * the new item charges them at the new price; with prorate off the old item
11
+ * keeps them and the new item starts charging at its next full period.
12
+ *
13
+ * Pure — validates and computes the dates; the command does the writes.
14
+ */
15
+
16
+ export type PriceChangeItem = {
17
+ type: string
18
+ isActive: boolean
19
+ deletedAt?: Date | string | null
20
+ billStartDate: Date | string
21
+ billEndDate?: Date | string | null
22
+ }
23
+
24
+ export type PriceChangePlan = {
25
+ /** New end date of the current item: the day before the change. */
26
+ oldItemEndDate: Date
27
+ /** Start of the new item: the effective date. */
28
+ newItemStartDate: Date
29
+ /** The new item keeps the original end date (null = open-ended). */
30
+ newItemEndDate: Date | null
31
+ }
32
+
33
+ export type PriceChangeError =
34
+ | 'billing.item.price_change_recurring_only'
35
+ | 'billing.item.price_change_inactive'
36
+ | 'billing.item.price_change_before_start'
37
+ | 'billing.item.price_change_after_end'
38
+
39
+ export function planPriceChange(
40
+ item: PriceChangeItem,
41
+ effectiveDate: Date | string,
42
+ ): { ok: true; plan: PriceChangePlan } | { ok: false; error: PriceChangeError } {
43
+ if (item.type !== 'recurring') return { ok: false, error: 'billing.item.price_change_recurring_only' }
44
+ if (!item.isActive || item.deletedAt) return { ok: false, error: 'billing.item.price_change_inactive' }
45
+ const effective = toUtcMidnight(effectiveDate)
46
+ const start = toUtcMidnight(item.billStartDate)
47
+ // The current item must keep at least one day.
48
+ if (effective.getTime() <= start.getTime()) return { ok: false, error: 'billing.item.price_change_before_start' }
49
+ const end = item.billEndDate ? toUtcMidnight(item.billEndDate) : null
50
+ if (end && effective.getTime() > end.getTime()) return { ok: false, error: 'billing.item.price_change_after_end' }
51
+ const oldItemEndDate = new Date(effective)
52
+ oldItemEndDate.setUTCDate(oldItemEndDate.getUTCDate() - 1)
53
+ return { ok: true, plan: { oldItemEndDate, newItemStartDate: effective, newItemEndDate: end } }
54
+ }
55
+
56
+ export { toIsoDate }
@@ -20,7 +20,10 @@ import type { BillPeriod } from './billPeriod'
20
20
  * `billed_to_date IS NULL` AND the item's `[bill_start_date,
21
21
  * bill_end_date]` window overlaps the bill period.
22
22
  *
23
- * `recurring` — one line per full cycle. Eligible only when:
23
+ * `recurring` — used by PREPAID period close only (Spec A, A-D1). The
24
+ * postpaid engine prices recurring items with `servicePeriods`
25
+ * (in advance / in arrears, proration, credits). Here: one line per
26
+ * full cycle. Eligible only when:
24
27
  * - `bill_start_date <= bill_period_start`
25
28
  * - AND (`bill_end_date IS NULL` OR `bill_end_date >= bill_period_end`)
26
29
  * - AND (`billed_to_date IS NULL` OR `billed_to_date < bill_period_end`)
@@ -0,0 +1,61 @@
1
+ import { lineAmountsFromGross, lineAmountsFromNet, type VatAmounts } from './vat'
2
+
3
+ /**
4
+ * Basis-aware line VAT — the single entry point for every billing line
5
+ * (Spec F, P-D3): invoice drafts, dry / test previews and prepaid debits.
6
+ *
7
+ * An item price is stored as typed, on its `price_basis`:
8
+ * - `net`: tax = round2(net × rate), gross = net + tax
9
+ * - `gross`: net = round2(gross / (1 + rate)), tax = gross − net
10
+ * so a gross the operator typed is exactly the gross on the document.
11
+ * Rounding happens once per line, on the extended amount
12
+ * (unit × quantity × proration), never per unit.
13
+ */
14
+
15
+ export type PriceBasis = 'net' | 'gross'
16
+
17
+ export type LineVatInput = {
18
+ /** Extended line amount on `priceBasis` (unit × quantity × proration). */
19
+ amount: number | string
20
+ priceBasis: PriceBasis
21
+ /** VAT rate in percent. */
22
+ taxRate: number | string
23
+ }
24
+
25
+ function toNumber(value: number | string): number {
26
+ const n = typeof value === 'number' ? value : Number.parseFloat(value)
27
+ return Number.isFinite(n) ? n : 0
28
+ }
29
+
30
+ export function lineVat(input: LineVatInput): VatAmounts {
31
+ const amount = toNumber(input.amount)
32
+ const rate = toNumber(input.taxRate)
33
+ return input.priceBasis === 'gross' ? lineAmountsFromGross(amount, rate) : lineAmountsFromNet(amount, rate)
34
+ }
35
+
36
+ /** Resolve a stored basis value (anything but `gross` reads as `net`). */
37
+ export function priceBasisOf(value: unknown): PriceBasis {
38
+ return value === 'gross' ? 'gross' : 'net'
39
+ }
40
+
41
+ /**
42
+ * Net unit price for `billing_invoice_lines.unit_price_net` (display only,
43
+ * 4 dp). Net basis keeps the typed unit price; gross basis derives it.
44
+ */
45
+ export function unitPriceNetOf(unitPrice: number | string, priceBasis: PriceBasis, taxRate: number | string): string {
46
+ const unit = toNumber(unitPrice)
47
+ if (priceBasis !== 'gross') return unit.toFixed(4)
48
+ const rate = toNumber(taxRate)
49
+ const net = rate > 0 ? unit / (1 + rate / 100) : unit
50
+ const sign = net < 0 ? -1 : 1
51
+ return ((sign * Math.round((Math.abs(net) + Number.EPSILON) * 10000)) / 10000).toFixed(4)
52
+ }
53
+
54
+ /**
55
+ * The counterpart of a typed unit price, for the ItemForm preview: the gross
56
+ * of a net price or the net of a gross price (2 dp, same rule as the lines).
57
+ */
58
+ export function counterpartAmount(amount: number | string, priceBasis: PriceBasis, taxRate: number | string): number {
59
+ const vat = lineVat({ amount, priceBasis, taxRate })
60
+ return priceBasis === 'gross' ? vat.net : vat.gross
61
+ }
@@ -0,0 +1,41 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { toCents } from './allocation'
3
+
4
+ /**
5
+ * Credit memo guard (Spec D, P-D4). Pure core + one aggregate query.
6
+ *
7
+ * Σ gross of posted credit memos on the original (incl. the new one)
8
+ * ≤ original gross + Σ gross of posted debit memos on it
9
+ *
10
+ * Evaluated inside the per-account lock at post (authoritative) and,
11
+ * without the lock, when a draft memo is created (early feedback).
12
+ */
13
+
14
+ export type CorrectionTotals = { creditCents: number; debitCents: number }
15
+
16
+ export function creditHeadroomCents(originalGrossCents: number, totals: CorrectionTotals): number {
17
+ return originalGrossCents + totals.debitCents - totals.creditCents
18
+ }
19
+
20
+ export function creditFits(originalGrossCents: number, totals: CorrectionTotals, newCreditCents: number): boolean {
21
+ return newCreditCents <= creditHeadroomCents(originalGrossCents, totals)
22
+ }
23
+
24
+ /** Σ gross of POSTED (non-draft) memos correcting an invoice, per type. */
25
+ export async function loadCorrectionTotals(
26
+ em: EntityManager,
27
+ scope: { tenantId: string; organizationId: string },
28
+ originalId: string,
29
+ opts: { excludeId?: string } = {},
30
+ ): Promise<CorrectionTotals> {
31
+ const rows = (await em.execute(
32
+ `SELECT document_type, COALESCE(SUM(total_gross), 0) AS gross
33
+ FROM billing_invoices
34
+ WHERE corrects_invoice_id = ? AND tenant_id = ? AND organization_id = ? AND deleted_at IS NULL
35
+ AND status <> 'draft' AND test_run = false AND id <> ?
36
+ GROUP BY document_type`,
37
+ [originalId, scope.tenantId, scope.organizationId, opts.excludeId ?? '00000000-0000-0000-0000-000000000000'],
38
+ )) as unknown as Array<{ document_type: string; gross: string }>
39
+ const of = (type: string) => toCents(rows?.find((r) => r.document_type === type)?.gross ?? '0')
40
+ return { creditCents: of('credit_memo'), debitCents: of('debit_memo') }
41
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Where an item page returns to (Spec E, D23).
3
+ *
4
+ * Billing items always belong to an account, so the account is the natural
5
+ * parent: back / after-save / after-delete land there. The only exception is
6
+ * an item opened from the global Items list (`?from=items`), which returns
7
+ * to that list so the operator keeps their place.
8
+ */
9
+
10
+ export const ITEMS_LIST_HREF = '/backend/billing/items'
11
+
12
+ /** Query value the global Items list appends when opening an item. */
13
+ export const FROM_ITEMS_LIST = 'items'
14
+
15
+ /** Tabs of the Billing Account workspace (Spec E / Spec F, F8), deep-linkable via `?tab=`. */
16
+ export const ACCOUNT_TABS = ['overview', 'items', 'usage', 'invoices', 'payments', 'balance', 'statements', 'runs'] as const
17
+ export type AccountTab = (typeof ACCOUNT_TABS)[number]
18
+
19
+ export function isAccountTab(value: unknown): value is AccountTab {
20
+ return typeof value === 'string' && (ACCOUNT_TABS as readonly string[]).includes(value)
21
+ }
22
+
23
+ /** The account workspace, optionally on one tab (`overview` is the default and is left out). */
24
+ export function accountHref(billAccountId: string, tab?: AccountTab): string {
25
+ const base = `/backend/billing/accounts/${encodeURIComponent(billAccountId)}`
26
+ return tab && tab !== 'overview' ? `${base}?tab=${tab}` : base
27
+ }
28
+
29
+ export function resolveItemReturnTarget(params: {
30
+ from?: string | null
31
+ billAccountId?: string | null
32
+ }): string {
33
+ if (params.from === FROM_ITEMS_LIST) return ITEMS_LIST_HREF
34
+ const accountId = (params.billAccountId ?? '').trim()
35
+ return accountId ? accountHref(accountId, 'items') : ITEMS_LIST_HREF
36
+ }
37
+
38
+ export function itemDetailHref(itemId: string, opts: { fromItemsList?: boolean } = {}): string {
39
+ const base = `${ITEMS_LIST_HREF}/${encodeURIComponent(itemId)}`
40
+ return opts.fromItemsList ? `${base}?from=${FROM_ITEMS_LIST}` : base
41
+ }
42
+
43
+ /**
44
+ * Where an invoice detail page returns to (Spec B): the account it was
45
+ * opened from (`?from=account`), otherwise the global Invoices list.
46
+ */
47
+ export const INVOICES_LIST_HREF = '/backend/billing/invoices'
48
+ export const FROM_ACCOUNT = 'account'
49
+
50
+ export function invoiceDetailHref(invoiceId: string, opts: { fromAccountId?: string | null } = {}): string {
51
+ const base = `${INVOICES_LIST_HREF}/${encodeURIComponent(invoiceId)}`
52
+ const accountId = (opts.fromAccountId ?? '').trim()
53
+ return accountId ? `${base}?from=${FROM_ACCOUNT}&accountId=${encodeURIComponent(accountId)}` : base
54
+ }
55
+
56
+ export function resolveInvoiceReturnTarget(params: { from?: string | null; accountId?: string | null }): string {
57
+ const accountId = (params.accountId ?? '').trim()
58
+ return params.from === FROM_ACCOUNT && accountId ? accountHref(accountId, 'invoices') : INVOICES_LIST_HREF
59
+ }
60
+
61
+ /** PDF of a billing document — frozen when posted, a watermarked preview otherwise. */
62
+ export function invoicePdfHref(invoiceId: string): string {
63
+ return `/api/billing/invoices/${encodeURIComponent(invoiceId)}/pdf`
64
+ }
@@ -0,0 +1,174 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+
3
+ /**
4
+ * Document numbering (Spec 0, decision E-D6 / P-D3).
5
+ *
6
+ * Each series has a mask, e.g. `INV/{YYYY}/{MM}/{SEQUENCE_NUM_MONTH:4}`:
7
+ * {YYYY} {YY} {MM} {DD} — parts of the issue date
8
+ * {SEQUENCE_NUM} — counter never resets
9
+ * {SEQUENCE_NUM_YEAR} — counter resets every year
10
+ * {SEQUENCE_NUM_MONTH} — counter resets every month
11
+ * …:N — optional zero-pad to N digits
12
+ *
13
+ * The counter row is `(tenant, org, series, scope_key)` where scope_key is
14
+ * '' / 'YYYY' / 'YYYY-MM'. A number is claimed only at post, by one upsert
15
+ * inside the caller's transaction — a rollback releases it (gap-free) and
16
+ * concurrent posts serialize on the row.
17
+ */
18
+
19
+ export const DOCUMENT_SERIES = ['invoice', 'credit_memo', 'receipt'] as const
20
+ export type DocumentSeries = (typeof DOCUMENT_SERIES)[number]
21
+
22
+ export const DEFAULT_NUMBER_MASKS: Record<DocumentSeries, string> = {
23
+ invoice: 'INV/{YYYY}/{MM}/{SEQUENCE_NUM_MONTH:4}',
24
+ credit_memo: 'CN/{YYYY}/{MM}/{SEQUENCE_NUM_MONTH:4}',
25
+ receipt: 'RC/{YYYY}/{MM}/{SEQUENCE_NUM_MONTH:4}',
26
+ }
27
+
28
+ export type SequenceScope = 'none' | 'year' | 'month'
29
+
30
+ export type ParsedMask = {
31
+ scope: SequenceScope
32
+ pad: number
33
+ hasYear: boolean
34
+ hasMonth: boolean
35
+ }
36
+
37
+ export type MaskError =
38
+ | 'mask_empty'
39
+ | 'mask_too_long'
40
+ | 'mask_unknown_token'
41
+ | 'mask_missing_sequence'
42
+ | 'mask_multiple_sequences'
43
+ | 'mask_year_scope_needs_year'
44
+ | 'mask_month_scope_needs_year_and_month'
45
+
46
+ export const MAX_MASK_LENGTH = 64
47
+
48
+ const TOKEN_RE = /\{([A-Z_]+)(?::(\d{1,2}))?\}/g
49
+ const DATE_TOKENS = new Set(['YYYY', 'YY', 'MM', 'DD'])
50
+ const SEQUENCE_TOKENS: Record<string, SequenceScope> = {
51
+ SEQUENCE_NUM: 'none',
52
+ SEQUENCE_NUM_YEAR: 'year',
53
+ SEQUENCE_NUM_MONTH: 'month',
54
+ }
55
+
56
+ function tokensOf(mask: string): Array<{ name: string; pad: number }> {
57
+ return Array.from(mask.matchAll(TOKEN_RE), (m) => ({ name: m[1], pad: m[2] ? Number(m[2]) : 0 }))
58
+ }
59
+
60
+ /** Validation errors for a mask; empty array = valid. */
61
+ export function validateMask(mask: string): MaskError[] {
62
+ const trimmed = (mask ?? '').trim()
63
+ if (!trimmed) return ['mask_empty']
64
+ const errors: MaskError[] = []
65
+ if (trimmed.length > MAX_MASK_LENGTH) errors.push('mask_too_long')
66
+ const tokens = tokensOf(trimmed)
67
+ // Any brace not consumed by a known-shaped token is an unknown token.
68
+ const stripped = trimmed.replace(TOKEN_RE, '')
69
+ if (/[{}]/.test(stripped) || tokens.some((t) => !DATE_TOKENS.has(t.name) && !(t.name in SEQUENCE_TOKENS))) {
70
+ errors.push('mask_unknown_token')
71
+ }
72
+ const sequences = tokens.filter((t) => t.name in SEQUENCE_TOKENS)
73
+ if (sequences.length === 0) errors.push('mask_missing_sequence')
74
+ if (sequences.length > 1) errors.push('mask_multiple_sequences')
75
+ const hasYear = tokens.some((t) => t.name === 'YYYY' || t.name === 'YY')
76
+ const hasMonth = tokens.some((t) => t.name === 'MM')
77
+ const scope = sequences[0] ? SEQUENCE_TOKENS[sequences[0].name] : 'none'
78
+ if (scope === 'year' && !hasYear) errors.push('mask_year_scope_needs_year')
79
+ if (scope === 'month' && (!hasYear || !hasMonth)) errors.push('mask_month_scope_needs_year_and_month')
80
+ return errors
81
+ }
82
+
83
+ export function parseMask(mask: string): ParsedMask {
84
+ const tokens = tokensOf(mask)
85
+ const sequence = tokens.find((t) => t.name in SEQUENCE_TOKENS)
86
+ return {
87
+ scope: sequence ? SEQUENCE_TOKENS[sequence.name] : 'none',
88
+ pad: sequence?.pad ?? 0,
89
+ hasYear: tokens.some((t) => t.name === 'YYYY' || t.name === 'YY'),
90
+ hasMonth: tokens.some((t) => t.name === 'MM'),
91
+ }
92
+ }
93
+
94
+ function datePart(date: Date, part: 'YYYY' | 'YY' | 'MM' | 'DD'): string {
95
+ const d = new Date(date)
96
+ switch (part) {
97
+ case 'YYYY':
98
+ return String(d.getUTCFullYear())
99
+ case 'YY':
100
+ return String(d.getUTCFullYear()).slice(-2)
101
+ case 'MM':
102
+ return String(d.getUTCMonth() + 1).padStart(2, '0')
103
+ case 'DD':
104
+ return String(d.getUTCDate()).padStart(2, '0')
105
+ }
106
+ }
107
+
108
+ /** Counter scope for a mask on a given issue date: '', 'YYYY' or 'YYYY-MM'. */
109
+ export function sequenceScopeKey(mask: string, issueDate: Date): string {
110
+ const { scope } = parseMask(mask)
111
+ if (scope === 'year') return datePart(issueDate, 'YYYY')
112
+ if (scope === 'month') return `${datePart(issueDate, 'YYYY')}-${datePart(issueDate, 'MM')}`
113
+ return ''
114
+ }
115
+
116
+ export function renderNumber(mask: string, params: { seq: number; issueDate: Date }): string {
117
+ return mask.replace(TOKEN_RE, (_match, name: string, pad?: string) => {
118
+ if (name in SEQUENCE_TOKENS) return String(params.seq).padStart(pad ? Number(pad) : 0, '0')
119
+ if (DATE_TOKENS.has(name)) return datePart(params.issueDate, name as 'YYYY' | 'YY' | 'MM' | 'DD')
120
+ return _match
121
+ })
122
+ }
123
+
124
+ export function maskFor(masks: Partial<Record<string, string>> | null | undefined, series: DocumentSeries): string {
125
+ const candidate = masks?.[series]
126
+ return candidate && validateMask(candidate).length === 0 ? candidate : DEFAULT_NUMBER_MASKS[series]
127
+ }
128
+
129
+ function randomHex(length: number): string {
130
+ let out = ''
131
+ while (out.length < length) out += Math.floor(Math.random() * 0x100000000).toString(16).padStart(8, '0')
132
+ return out.slice(0, length)
133
+ }
134
+
135
+ /** Temporary number for an unposted real draft (no sequence consumed). */
136
+ export function draftNumber(): string {
137
+ return `DRAFT-${randomHex(8).toUpperCase()}`
138
+ }
139
+
140
+ /** Number for test-run drafts — never posted, never numbered. */
141
+ export function testNumber(): string {
142
+ return `TEST-${Date.now().toString(16)}-${randomHex(8)}`
143
+ }
144
+
145
+ export type NumberScope = { tenantId: string; organizationId: string }
146
+
147
+ /**
148
+ * Claims the next number of a series. MUST run inside the caller's
149
+ * transaction (post command / receipt capture) so a rollback releases it.
150
+ */
151
+ export async function claimDocumentNumber(
152
+ em: EntityManager,
153
+ scope: NumberScope,
154
+ series: DocumentSeries,
155
+ mask: string,
156
+ issueDate: Date,
157
+ ): Promise<{ number: string; seq: number; scopeKey: string }> {
158
+ const scopeKey = sequenceScopeKey(mask, issueDate)
159
+ const now = new Date()
160
+ const rows = (await em.execute(
161
+ `INSERT INTO billing_document_sequences
162
+ (tenant_id, organization_id, series, scope_key, next_value, created_at, updated_at)
163
+ VALUES (?, ?, ?, ?, 2, ?, ?)
164
+ ON CONFLICT (tenant_id, organization_id, series, scope_key)
165
+ DO UPDATE SET next_value = billing_document_sequences.next_value + 1, updated_at = EXCLUDED.updated_at
166
+ RETURNING next_value - 1 AS seq`,
167
+ [scope.tenantId, scope.organizationId, series, scopeKey, now, now],
168
+ )) as unknown as Array<{ seq: number | string }> | undefined
169
+ const seq = Number(rows?.[0]?.seq)
170
+ if (!Number.isFinite(seq) || seq < 1) {
171
+ throw new Error(`Failed to claim a ${series} number`)
172
+ }
173
+ return { number: renderNumber(mask, { seq, issueDate }), seq, scopeKey }
174
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Overdue is computed, never stored (Spec B, B-D6):
3
+ *
4
+ * status ∈ {posted, partially_paid} AND due_date < today AND outstanding > 0
5
+ *
6
+ * Receivable documents only (invoices and debit memos). One definition shared
7
+ * by the in-memory predicate (UI / tests) and the SQL fragment (invoice list,
8
+ * account summary), so the two can never disagree. No worker until dunning.
9
+ */
10
+
11
+ export const OPEN_STATUSES = ['posted', 'partially_paid'] as const
12
+ export const RECEIVABLE_DOCUMENT_TYPES = ['invoice', 'debit_memo'] as const
13
+
14
+ export type OverdueCandidate = {
15
+ status: string
16
+ documentType?: string | null
17
+ dueDate: Date | string | null | undefined
18
+ outstanding: string | number | null | undefined
19
+ }
20
+
21
+ function dayNumber(value: Date | string): number {
22
+ const date = typeof value === 'string' ? new Date(`${value.slice(0, 10)}T00:00:00Z`) : value
23
+ return Math.floor(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()) / 86_400_000)
24
+ }
25
+
26
+ export function isOverdue(doc: OverdueCandidate, today: Date): boolean {
27
+ return daysOverdue(doc, today) > 0
28
+ }
29
+
30
+ /** Whole days past the due date; 0 when not overdue. */
31
+ export function daysOverdue(doc: OverdueCandidate, today: Date): number {
32
+ if (!(OPEN_STATUSES as readonly string[]).includes(doc.status)) return 0
33
+ if (doc.documentType && !(RECEIVABLE_DOCUMENT_TYPES as readonly string[]).includes(doc.documentType)) return 0
34
+ if (!doc.dueDate) return 0
35
+ const outstanding = typeof doc.outstanding === 'number' ? doc.outstanding : Number.parseFloat(doc.outstanding ?? '0')
36
+ if (!(outstanding > 0)) return 0
37
+ return Math.max(0, dayNumber(today) - dayNumber(doc.dueDate))
38
+ }
39
+
40
+ /** `YYYY-MM-DD` of a date in UTC — the `today` parameter for SQL. */
41
+ export function isoDay(date: Date): string {
42
+ return date.toISOString().slice(0, 10)
43
+ }
44
+
45
+ /**
46
+ * SQL predicate for overdue documents on alias `alias`. Bind one parameter:
47
+ * today as `YYYY-MM-DD`.
48
+ */
49
+ export function overdueSql(alias: string): string {
50
+ return (
51
+ `(${alias}.status IN ('posted', 'partially_paid')` +
52
+ ` AND ${alias}.document_type IN ('invoice', 'debit_memo')` +
53
+ ` AND ${alias}.due_date < ?::date AND ${alias}.outstanding > 0)`
54
+ )
55
+ }
56
+
57
+ /** SQL expression for days overdue (0 when not overdue). Bind today twice. */
58
+ export function daysOverdueSql(alias: string): string {
59
+ return `(CASE WHEN ${overdueSql(alias)} THEN (?::date - ${alias}.due_date) ELSE 0 END)`
60
+ }