@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,95 @@
1
1
  # @dainamite/billing
2
2
 
3
+ ## 0.21.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d126be2: Billing accounts for persons (B2C), an invoice-language picklist, and pages that return to their billing account.
8
+
9
+ **`@dainamite/billing`**
10
+
11
+ - **B2C accounts.** `BillingAccount.customer_kind` (`company` | `person`, default `company`) records which customers collection `customer_id` points into. It is set on create (`customerKind`) and immutable afterwards, like `customerId`. `GET /api/billing/accounts` returns `customer_kind` and accepts a `customerKind` filter. Migration `Migration20261114000000_billing_customer_kind` adds the column (additive; existing rows become `company`). Run `yarn mercato db migrate`.
12
+ - **Account form.** A Company / Person switch picks the customer lookup (`/api/customers/companies` or `/api/customers/people`). Picking a customer prefills name, invoice email and the primary address into empty fields only. Prefill that the operator has not edited is rolled back when they pick another customer.
13
+ - **Account list and detail** show the customer's name with a company / person icon (one batched lookup per kind per page) and add a Customer type filter. The detail links to the customer record. If the customers module is unavailable, the raw id is shown.
14
+ - **Invoice language** is now a picklist of the languages the invoice template supports (`en`, `pl`).
15
+ **Behaviour change:** `invoiceLanguage` values other than `en` / `pl` are now rejected with 400 on create and update. On create, `invoiceLanguage` is optional and defaults to `en`.
16
+ - **Navigation.** Item pages opened from a billing account return to that account (back link, after delete). Creating an item from an account returns to the account after save or cancel. Items opened from the global Items list (`?from=items`) still return to the list.
17
+
18
+ **`@dainamite/cpq-billing-connector`**
19
+
20
+ - Passes an optional `customerKind` from `cpq.subscription.activated` to the billing account it creates.
21
+ - Normalises `invoiceLanguage` and falls back to `en` for languages billing does not support, so onboarding never fails on an unsupported code.
22
+
23
+ - 486db4d: Bill runs catch up on every due cycle, bill recurring items in advance or in arrears, and prorate partial periods (billing Spec A).
24
+
25
+ **`@dainamite/billing`**
26
+
27
+ - **Every run catches up.** Manual or scheduled, in real, test or dry mode, a run bills every due bill date up to `asOfDate`, with one draft per bill date (capped at 60 per account). Test and dry runs simulate every cycle in memory without changing the account. **Breaking:** the `catchUp` trigger flag and the `catch_up` run column/response field are removed. A `catchUp` sent by an old client is ignored.
28
+ - **Charge timing per item.** Recurring items carry `chargeTiming` (`in_advance` by default, or `in_arrears`). **Behaviour change:** recurring items are now billed in advance by default, so `next_bill_date` marks the start of the period billed.
29
+ - **Proration per item.** `prorate` defaults to `true`. A partial period (the item starts or ends mid-cycle) is charged by calendar days instead of being skipped. An in-advance item that ends after it was billed gets a negative credit line for the unused days. With `prorate: false`, partial periods are skipped and reported.
30
+ - **Run outcomes** carry `cycles` (bill date, service period, status, lines, net, invoice). `warnings.skipped_items` lists items that were not billed and why. The run summary adds `cycles_processed`. The run detail page shows a Cycles table and a Skipped items table.
31
+ - **Draft invoices** carry `metadata.bill_date`, the de-duplication key, together with `test_run`. Test drafts and real drafts never block each other. Lines carry `service_period_start` / `service_period_end`, plus `proration` and `credit_for` where they apply.
32
+ - **Item form** gets Charge timing and Prorate partial periods fields. The items list gets a Timing column.
33
+ - **Migration** `Migration20261114100000_billing_cycle_semantics`. Run `yarn mercato db migrate`. See `MIGRATION.md`; reseeding is recommended.
34
+
35
+ **`@dainamite/cpq-billing-connector`**
36
+
37
+ - **Proration moves to billing.** `cpq.subscription.amended` no longer posts `one_time` proration items, because the billing engine prorates. Release together with billing: an older connector would double-charge.
38
+ - **First bill date.** New billing accounts start billing on the activation date (they used to start one month later), so the first period is billed in advance.
39
+ - Removes `lib/prorationHelper` and the runtime import of billing's `cycleContaining`.
40
+
41
+ - c8a1bf7: Billing invoices become real VAT documents with a PDF and email delivery (billing Spec B).
42
+
43
+ - **VAT per line.** Billing items carry `taxRate` (filled from the new organization default, 23 %, when omitted). Every invoice line gets net / VAT / gross; proration and credit lines inherit the item rate; manual lines accept `taxRate`. Existing items are migrated at 0 % (no silent re-pricing).
44
+ - **Seller settings.** Settings → Billing adds the seller block, bank / IBAN (checksum validated) / BIC, default payment terms and VAT rate, invoice footer and logo. **Posting is refused with 409 `billing.settings.incomplete` until legal name, address, tax id and IBAN are filled.**
45
+ - **Due dates and overdue.** Posting sets `due_date` (account terms or the default). The invoice list filters by `overdue`, `dueFrom`, `dueTo` and shows days overdue; the account page gets an Invoices section and `GET /api/billing/accounts/{id}/summary`.
46
+ - **PDF.** `GET /api/billing/invoices/{id}/pdf` — built-in A4 template in the account's invoice language (en / pl), frozen as an attachment at post; drafts are watermarked previews.
47
+ - **Email.** `POST /api/billing/invoices/{id}/send` (feature `billing.invoice.send`) and automatic sending per account (`autoSendInvoice`), through the platform `sendEmail()`. Deliveries are logged on the invoice; events `billing.invoice.sent` / `billing.invoice.send_failed`.
48
+ - **Dependencies:** `requires` adds `attachments`; new dependencies `pdf-lib` and `@pdf-lib/fontkit`.
49
+ - Connector: `ItemCreatePayload` accepts an optional `taxRate` (unused until CPQ exposes a per-charge rate).
50
+ - Run `yarn mercato db migrate` (`Migration20261116000000_billing_invoice_documents`).
51
+
52
+ - 6c138f8: Billing owns its documents. Invoices, credit / debit memos and top-up receipts are no longer written to Open Mercato core `sales` (billing Spec 0).
53
+
54
+ - **New tables:** `billing_invoices`, `billing_invoice_lines`, `billing_document_sequences`, `billing_settings`. Run `yarn mercato db migrate`. No data is migrated: reseed, or see `MIGRATION.md` for removing old billing rows from `sales_invoices`.
55
+ - **Numbering at post.** Drafts carry `DRAFT-…`, and the series number is claimed when the invoice is posted (gap-free, inside the post transaction). A mask per series is set on the new **Billing settings** page (`/backend/config/billing`), using `{YYYY} {YY} {MM} {DD}` plus one counter token: `{SEQUENCE_NUM}` (never resets), `{SEQUENCE_NUM_YEAR}` or `{SEQUENCE_NUM_MONTH}`, with an optional `:N` zero-pad. The default is `INV/{YYYY}/{MM}/{SEQUENCE_NUM_MONTH:4}`. A mask that repeats an issued number fails the post with 409 `billing.invoice.number_conflict`.
56
+ - **Breaking:**
57
+ - Invoice API fields are renamed to billing names (`number`, `total_gross`, `outstanding`, `bill_date`, `period_start`, …). The full map is in `MIGRATION.md`.
58
+ - The `void` status is removed. Statuses are now `draft | posted | partially_paid | paid | credited`.
59
+ - The account export moves to `schemaVersion: 3`.
60
+ - **Top-up receipts** are billing documents (`document_type = 'receipt'`, `RC/…` series). VAT is split by billing's own `lib/vat.ts`.
61
+ - **Prepaid settings** move from core `configs` to per-organization billing settings (same defaults).
62
+ - **Dependencies:** `requires` is now `['auth', 'directory']`. `payment_gateways` is optional: without it, online top-ups return 501 `billing.topup.gateway_unavailable` and the Top up button is hidden. The unused `billing.uom` dictionary seed is removed. An isolation test fails the build if billing imports core `sales`, `configs`, `dictionaries` or `payment_gateways`.
63
+ - **New permissions:** `billing.settings.view` and `billing.settings.manage`.
64
+
65
+ - 418d67c: Credit and debit memos (billing Spec D): posted invoices are corrected only by referenced corrective documents.
66
+
67
+ - Invoice page: **Create credit memo** (pick lines, edit quantity / unit net, "Full credit", required reason) and **Create debit memo**; draft memos can be deleted, posted ones never (no void). A Corrections section lists the memos of an invoice.
68
+ - Posting a credit memo (`CN/…` series) settles the corrected invoice through the allocation model; any remainder becomes account credit applied to the oldest open invoice. An invoice settled entirely by credit becomes `credited`. Debit memos (`invoice` series) are payable: due date, overdue, matched by number in bank imports.
69
+ - Guard: total credits on an invoice never exceed its gross plus its posted debit memos (409 `billing.memo.exceeds_invoice`).
70
+ - PDF memo variant ("Corrects invoice X of DATE", reason, settlement text); list Type filter / column (credit memo totals shown negative).
71
+ - Events `billing.credit_memo.posted`, `billing.debit_memo.posted`, `billing.invoice.credited`; `billing.invoice.posted` fires for invoices only. New permission `billing.invoice.correct`.
72
+ - Run `yarn mercato db migrate` (`Migration20261118000000_billing_memos`).
73
+
74
+ - 3dc3fa5: Operator feedback round 1 (billing Spec F) and the Billing Account workspace (Spec E phases 2–3).
75
+
76
+ - Bill Runs: **Delete run** for test and dry runs (removes the run, its outcomes and its test invoices; real runs are never deleted — `DELETE /api/billing/runs/{id}`, permission `billing.run.delete`). "Wipe test invoices" moved to the run page; runs list gets a Mode filter and a row action.
77
+ - Items: the account currency is shown next to every price; a **Net | Gross** switch stores the price as typed (`priceBasis`) and the invoice reproduces a typed gross exactly. Fixed: editing an item's VAT rate is saved again.
78
+ - Prepaid: one run closes every due bill date (also in test / dry); recurring items honour in advance / in arrears and proration; run outcomes show prepaid cycles and skipped items; an existing statement no longer blocks the account.
79
+ - Prepaid VAT: new setting **When is VAT charged** — at top-up (default; item amounts debited as entered) or at billing (receipts without VAT, debits gross at the item rate, statement VAT summary).
80
+ - Account workspace: summary cards, deep-linkable tabs (Overview, Items, Usage, Invoices, Payments / Balance & top-ups, Statements, Bill runs), **Run billing** dialog with inline results; account list Balance due / Overdue columns and Has overdue filter.
81
+ - Settings → Billing is listed in the Settings sidebar; number-mask help explains `:N` zero-padding.
82
+ - Run `yarn mercato db migrate` (`Migration20261119000000_billing_operator_feedback`) and `yarn mercato auth sync-role-acls`.
83
+
84
+ - 418d67c: Billing payments (Spec C): a billing-owned payment register with bank CSV import, auto-matching by invoice number, allocations and account credit.
85
+
86
+ - New **Billing → Payments** page: upload a bank CSV (`date,amount,currency,reference,payer_name,payer_account`) or record a payment; each payment shows its match outcome, allocations and actions (assign to account, allocate, un-allocate, ignore / restore, convert to a prepaid top-up). Upload batches keep counts and row errors.
87
+ - Invoices move `posted → partially_paid → paid` from allocations (always recomputed); overpayments become account credit that is applied to the oldest open invoice, on import and when an invoice is posted.
88
+ - Gateway captures (`payment_gateways.payment.captured`) now create a payment and allocate it instead of flipping the invoice status directly.
89
+ - Invoice detail shows a Payments section; the account page shows its payments and the unapplied credit.
90
+ - New permissions `billing.payment.view`, `billing.payment.manage`, `billing.payment.import`; CLI `mercato billing payments-sample` builds a demo bank file from posted invoices.
91
+ - Run `yarn mercato db migrate` (`Migration20261117000000_billing_payments`).
92
+
3
93
  ## 0.20.0
4
94
 
5
95
  ### Minor Changes
@@ -8,9 +8,18 @@ const features = [
8
8
  { id: "billing.run.trigger", title: "Trigger Bill Run", module: "billing" },
9
9
  { id: "billing.run.dry_run", title: "Trigger Bill Run (dry-run)", module: "billing" },
10
10
  { id: "billing.run.view", title: "View Bill Run History", module: "billing" },
11
+ // Spec F — delete test / dry runs (real runs are never deleted)
12
+ { id: "billing.run.delete", title: "Delete Test / Dry Bill Runs", module: "billing" },
11
13
  { id: "billing.invoice.post", title: "Post Draft Invoice", module: "billing" },
12
14
  { id: "billing.invoice.edit_draft", title: "Edit Draft Invoice", module: "billing" },
13
15
  { id: "billing.invoice.view", title: "View Billing Invoices", module: "billing" },
16
+ { id: "billing.invoice.send", title: "Email Billing Invoices", module: "billing" },
17
+ // Spec D — corrective documents
18
+ { id: "billing.invoice.correct", title: "Create Credit / Debit Memos", module: "billing" },
19
+ // Spec C — payments
20
+ { id: "billing.payment.view", title: "View Billing Payments", module: "billing" },
21
+ { id: "billing.payment.manage", title: "Manage Billing Payments (assign, allocate, ignore, convert)", module: "billing" },
22
+ { id: "billing.payment.import", title: "Import Bank Payments (CSV)", module: "billing" },
14
23
  // ─── Prepaid balance billing (SPEC-002) ──────────────────────
15
24
  { id: "billing.balance.view", title: "View Balance & Transactions", module: "billing" },
16
25
  { id: "billing.balance.adjust", title: "Adjust Balance (manual credit/debit)", module: "billing" },
@@ -18,7 +27,10 @@ const features = [
18
27
  { id: "billing.topup.view", title: "View Account Top-ups", module: "billing" },
19
28
  { id: "billing.statement.view", title: "View Consumption Statements", module: "billing" },
20
29
  { id: "billing.statement.generate", title: "Generate Consumption Statement", module: "billing" },
21
- { id: "billing.credit.view", title: "View Account Credit Status", module: "billing" }
30
+ { id: "billing.credit.view", title: "View Account Credit Status", module: "billing" },
31
+ // ─── Billing settings (Spec 0) ───────────────────────────────
32
+ { id: "billing.settings.view", title: "View Billing Settings", module: "billing" },
33
+ { id: "billing.settings.manage", title: "Manage Billing Settings", module: "billing" }
22
34
  ];
23
35
  var acl_default = features;
24
36
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/billing/acl.ts"],
4
- "sourcesContent": ["export const features = [\n { id: 'billing.account.manage', title: 'Manage Billing Accounts', module: 'billing' },\n { id: 'billing.account.view', title: 'View Billing Accounts', module: 'billing' },\n { id: 'billing.item.manage', title: 'Manage Billing Items', module: 'billing' },\n { id: 'billing.item.view', title: 'View Billing Items', module: 'billing' },\n { id: 'billing.usage.ingest', title: 'Ingest Usage Records', module: 'billing' },\n { id: 'billing.usage.view', title: 'View Usage Records', module: 'billing' },\n { id: 'billing.run.trigger', title: 'Trigger Bill Run', module: 'billing' },\n { id: 'billing.run.dry_run', title: 'Trigger Bill Run (dry-run)', module: 'billing' },\n { id: 'billing.run.view', title: 'View Bill Run History', module: 'billing' },\n { id: 'billing.invoice.post', title: 'Post Draft Invoice', module: 'billing' },\n { id: 'billing.invoice.edit_draft', title: 'Edit Draft Invoice', module: 'billing' },\n { id: 'billing.invoice.view', title: 'View Billing Invoices', module: 'billing' },\n // \u2500\u2500\u2500 Prepaid balance billing (SPEC-002) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n { id: 'billing.balance.view', title: 'View Balance & Transactions', module: 'billing' },\n { id: 'billing.balance.adjust', title: 'Adjust Balance (manual credit/debit)', module: 'billing' },\n { id: 'billing.topup.create', title: 'Initiate Account Top-up', module: 'billing' },\n { id: 'billing.topup.view', title: 'View Account Top-ups', module: 'billing' },\n { id: 'billing.statement.view', title: 'View Consumption Statements', module: 'billing' },\n { id: 'billing.statement.generate', title: 'Generate Consumption Statement', module: 'billing' },\n { id: 'billing.credit.view', title: 'View Account Credit Status', module: 'billing' },\n] as const\n\nexport default features\n"],
5
- "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,0BAA0B,OAAO,2BAA2B,QAAQ,UAAU;AAAA,EACpF,EAAE,IAAI,wBAAwB,OAAO,yBAAyB,QAAQ,UAAU;AAAA,EAChF,EAAE,IAAI,uBAAuB,OAAO,wBAAwB,QAAQ,UAAU;AAAA,EAC9E,EAAE,IAAI,qBAAqB,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EAC1E,EAAE,IAAI,wBAAwB,OAAO,wBAAwB,QAAQ,UAAU;AAAA,EAC/E,EAAE,IAAI,sBAAsB,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EAC3E,EAAE,IAAI,uBAAuB,OAAO,oBAAoB,QAAQ,UAAU;AAAA,EAC1E,EAAE,IAAI,uBAAuB,OAAO,8BAA8B,QAAQ,UAAU;AAAA,EACpF,EAAE,IAAI,oBAAoB,OAAO,yBAAyB,QAAQ,UAAU;AAAA,EAC5E,EAAE,IAAI,wBAAwB,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EAC7E,EAAE,IAAI,8BAA8B,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EACnF,EAAE,IAAI,wBAAwB,OAAO,yBAAyB,QAAQ,UAAU;AAAA;AAAA,EAEhF,EAAE,IAAI,wBAAwB,OAAO,+BAA+B,QAAQ,UAAU;AAAA,EACtF,EAAE,IAAI,0BAA0B,OAAO,wCAAwC,QAAQ,UAAU;AAAA,EACjG,EAAE,IAAI,wBAAwB,OAAO,2BAA2B,QAAQ,UAAU;AAAA,EAClF,EAAE,IAAI,sBAAsB,OAAO,wBAAwB,QAAQ,UAAU;AAAA,EAC7E,EAAE,IAAI,0BAA0B,OAAO,+BAA+B,QAAQ,UAAU;AAAA,EACxF,EAAE,IAAI,8BAA8B,OAAO,kCAAkC,QAAQ,UAAU;AAAA,EAC/F,EAAE,IAAI,uBAAuB,OAAO,8BAA8B,QAAQ,UAAU;AACtF;AAEA,IAAO,cAAQ;",
4
+ "sourcesContent": ["export const features = [\n { id: 'billing.account.manage', title: 'Manage Billing Accounts', module: 'billing' },\n { id: 'billing.account.view', title: 'View Billing Accounts', module: 'billing' },\n { id: 'billing.item.manage', title: 'Manage Billing Items', module: 'billing' },\n { id: 'billing.item.view', title: 'View Billing Items', module: 'billing' },\n { id: 'billing.usage.ingest', title: 'Ingest Usage Records', module: 'billing' },\n { id: 'billing.usage.view', title: 'View Usage Records', module: 'billing' },\n { id: 'billing.run.trigger', title: 'Trigger Bill Run', module: 'billing' },\n { id: 'billing.run.dry_run', title: 'Trigger Bill Run (dry-run)', module: 'billing' },\n { id: 'billing.run.view', title: 'View Bill Run History', module: 'billing' },\n // Spec F \u2014 delete test / dry runs (real runs are never deleted)\n { id: 'billing.run.delete', title: 'Delete Test / Dry Bill Runs', module: 'billing' },\n { id: 'billing.invoice.post', title: 'Post Draft Invoice', module: 'billing' },\n { id: 'billing.invoice.edit_draft', title: 'Edit Draft Invoice', module: 'billing' },\n { id: 'billing.invoice.view', title: 'View Billing Invoices', module: 'billing' },\n { id: 'billing.invoice.send', title: 'Email Billing Invoices', module: 'billing' },\n // Spec D \u2014 corrective documents\n { id: 'billing.invoice.correct', title: 'Create Credit / Debit Memos', module: 'billing' },\n // Spec C \u2014 payments\n { id: 'billing.payment.view', title: 'View Billing Payments', module: 'billing' },\n { id: 'billing.payment.manage', title: 'Manage Billing Payments (assign, allocate, ignore, convert)', module: 'billing' },\n { id: 'billing.payment.import', title: 'Import Bank Payments (CSV)', module: 'billing' },\n // \u2500\u2500\u2500 Prepaid balance billing (SPEC-002) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n { id: 'billing.balance.view', title: 'View Balance & Transactions', module: 'billing' },\n { id: 'billing.balance.adjust', title: 'Adjust Balance (manual credit/debit)', module: 'billing' },\n { id: 'billing.topup.create', title: 'Initiate Account Top-up', module: 'billing' },\n { id: 'billing.topup.view', title: 'View Account Top-ups', module: 'billing' },\n { id: 'billing.statement.view', title: 'View Consumption Statements', module: 'billing' },\n { id: 'billing.statement.generate', title: 'Generate Consumption Statement', module: 'billing' },\n { id: 'billing.credit.view', title: 'View Account Credit Status', module: 'billing' },\n // \u2500\u2500\u2500 Billing settings (Spec 0) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n { id: 'billing.settings.view', title: 'View Billing Settings', module: 'billing' },\n { id: 'billing.settings.manage', title: 'Manage Billing Settings', module: 'billing' },\n] as const\n\nexport default features\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,0BAA0B,OAAO,2BAA2B,QAAQ,UAAU;AAAA,EACpF,EAAE,IAAI,wBAAwB,OAAO,yBAAyB,QAAQ,UAAU;AAAA,EAChF,EAAE,IAAI,uBAAuB,OAAO,wBAAwB,QAAQ,UAAU;AAAA,EAC9E,EAAE,IAAI,qBAAqB,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EAC1E,EAAE,IAAI,wBAAwB,OAAO,wBAAwB,QAAQ,UAAU;AAAA,EAC/E,EAAE,IAAI,sBAAsB,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EAC3E,EAAE,IAAI,uBAAuB,OAAO,oBAAoB,QAAQ,UAAU;AAAA,EAC1E,EAAE,IAAI,uBAAuB,OAAO,8BAA8B,QAAQ,UAAU;AAAA,EACpF,EAAE,IAAI,oBAAoB,OAAO,yBAAyB,QAAQ,UAAU;AAAA;AAAA,EAE5E,EAAE,IAAI,sBAAsB,OAAO,+BAA+B,QAAQ,UAAU;AAAA,EACpF,EAAE,IAAI,wBAAwB,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EAC7E,EAAE,IAAI,8BAA8B,OAAO,sBAAsB,QAAQ,UAAU;AAAA,EACnF,EAAE,IAAI,wBAAwB,OAAO,yBAAyB,QAAQ,UAAU;AAAA,EAChF,EAAE,IAAI,wBAAwB,OAAO,0BAA0B,QAAQ,UAAU;AAAA;AAAA,EAEjF,EAAE,IAAI,2BAA2B,OAAO,+BAA+B,QAAQ,UAAU;AAAA;AAAA,EAEzF,EAAE,IAAI,wBAAwB,OAAO,yBAAyB,QAAQ,UAAU;AAAA,EAChF,EAAE,IAAI,0BAA0B,OAAO,+DAA+D,QAAQ,UAAU;AAAA,EACxH,EAAE,IAAI,0BAA0B,OAAO,8BAA8B,QAAQ,UAAU;AAAA;AAAA,EAEvF,EAAE,IAAI,wBAAwB,OAAO,+BAA+B,QAAQ,UAAU;AAAA,EACtF,EAAE,IAAI,0BAA0B,OAAO,wCAAwC,QAAQ,UAAU;AAAA,EACjG,EAAE,IAAI,wBAAwB,OAAO,2BAA2B,QAAQ,UAAU;AAAA,EAClF,EAAE,IAAI,sBAAsB,OAAO,wBAAwB,QAAQ,UAAU;AAAA,EAC7E,EAAE,IAAI,0BAA0B,OAAO,+BAA+B,QAAQ,UAAU;AAAA,EACxF,EAAE,IAAI,8BAA8B,OAAO,kCAAkC,QAAQ,UAAU;AAAA,EAC/F,EAAE,IAAI,uBAAuB,OAAO,8BAA8B,QAAQ,UAAU;AAAA;AAAA,EAEpF,EAAE,IAAI,yBAAyB,OAAO,yBAAyB,QAAQ,UAAU;AAAA,EACjF,EAAE,IAAI,2BAA2B,OAAO,2BAA2B,QAAQ,UAAU;AACvF;AAEA,IAAO,cAAQ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,52 @@
1
+ import { NextResponse } from "next/server";
2
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { BillingAccount } from "../../../../data/entities.js";
5
+ import { getAccountWorkspaceSummary } from "../../../../lib/accountSummary.js";
6
+ const metadata = {
7
+ GET: { requireAuth: true, requireFeatures: ["billing.account.view"] }
8
+ };
9
+ async function GET(req, { params }) {
10
+ try {
11
+ const container = await createRequestContainer();
12
+ const auth = await getAuthFromRequest(req);
13
+ if (!auth || !auth.tenantId) {
14
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
15
+ }
16
+ const organizationId = auth.orgId ?? null;
17
+ if (!organizationId) {
18
+ return NextResponse.json({ error: "Organization scope required" }, { status: 403 });
19
+ }
20
+ const { id: accountId } = await params;
21
+ const em = container.resolve("em").fork();
22
+ const account = await em.findOne(BillingAccount, {
23
+ id: accountId,
24
+ tenantId: auth.tenantId,
25
+ organizationId,
26
+ deletedAt: null
27
+ });
28
+ if (!account) {
29
+ return NextResponse.json({ error: "Account not found" }, { status: 404 });
30
+ }
31
+ return NextResponse.json(await getAccountWorkspaceSummary(em, account));
32
+ } catch (err) {
33
+ console.error("[billing/accounts/summary.GET]", err);
34
+ return NextResponse.json({ error: "Internal error" }, { status: 500 });
35
+ }
36
+ }
37
+ const openApi = {
38
+ methods: {
39
+ GET: {
40
+ tags: ["Billing"],
41
+ summary: "Get a billing account receivables summary",
42
+ parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "uuid" } }],
43
+ description: "Returns `{ bill_account_id, currency_code, open_amount, overdue_amount, overdue_count, credit_amount }` over posted / partially paid invoices and debit memos (test runs excluded). Overdue = past due date with outstanding > 0. `credit_amount` = unallocated payments + open credit memos. Spec F (account workspace) adds `billing_mode`, `bill_cycle`, `next_bill_date`, `last_invoice` (`{ id, number, issue_date, total_gross }` or null) and, for prepaid accounts, `prepaid { balance, balance_status, credit_limit, last_statement }` (null for postpaid)."
44
+ }
45
+ }
46
+ };
47
+ export {
48
+ GET,
49
+ metadata,
50
+ openApi
51
+ };
52
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/billing/api/accounts/%5Bid%5D/summary/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { BillingAccount } from '../../../../data/entities'\nimport { getAccountWorkspaceSummary } from '../../../../lib/accountSummary'\n\n/**\n * `GET /api/billing/accounts/[id]/summary` \u2014 what the customer owes (Spec B\n * AR summary; Spec E extends the same endpoint for the account workspace).\n * One aggregate query; see `lib/accountSummary.ts`.\n */\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['billing.account.view'] },\n}\n\nexport async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = (auth as { orgId?: string }).orgId ?? null\n if (!organizationId) {\n return NextResponse.json({ error: 'Organization scope required' }, { status: 403 })\n }\n const { id: accountId } = await params\n const em = (container.resolve('em') as EntityManager).fork()\n const account = await em.findOne(BillingAccount, {\n id: accountId,\n tenantId: auth.tenantId,\n organizationId,\n deletedAt: null,\n })\n if (!account) {\n return NextResponse.json({ error: 'Account not found' }, { status: 404 })\n }\n return NextResponse.json(await getAccountWorkspaceSummary(em, account))\n } catch (err) {\n console.error('[billing/accounts/summary.GET]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n\nexport const openApi = {\n methods: {\n GET: {\n tags: ['Billing'],\n summary: 'Get a billing account receivables summary',\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string', format: 'uuid' } }],\n description:\n 'Returns `{ bill_account_id, currency_code, open_amount, overdue_amount, overdue_count, ' +\n 'credit_amount }` over posted / partially paid invoices and debit memos (test runs excluded). ' +\n 'Overdue = past due date with outstanding > 0. `credit_amount` = unallocated payments + open credit memos. ' +\n 'Spec F (account workspace) adds `billing_mode`, `bill_cycle`, `next_bill_date`, `last_invoice` ' +\n '(`{ id, number, issue_date, total_gross }` or null) and, for prepaid accounts, `prepaid { balance, ' +\n 'balance_status, credit_limit, last_statement }` (null for postpaid).',\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAC/B,SAAS,kCAAkC;AAQpC,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,sBAAsB,EAAE;AACtE;AAEA,eAAsB,IAAI,KAAc,EAAE,OAAO,GAAwC;AACvF,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,QAAI,CAAC,QAAQ,CAAC,KAAK,UAAU;AAC3B,aAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACrE;AACA,UAAM,iBAAkB,KAA4B,SAAS;AAC7D,QAAI,CAAC,gBAAgB;AACnB,aAAO,aAAa,KAAK,EAAE,OAAO,8BAA8B,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACpF;AACA,UAAM,EAAE,IAAI,UAAU,IAAI,MAAM;AAChC,UAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,UAAM,UAAU,MAAM,GAAG,QAAQ,gBAAgB;AAAA,MAC/C,IAAI;AAAA,MACJ,UAAU,KAAK;AAAA,MACf;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AACD,QAAI,CAAC,SAAS;AACZ,aAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1E;AACA,WAAO,aAAa,KAAK,MAAM,2BAA2B,IAAI,OAAO,CAAC;AAAA,EACxE,SAAS,KAAK;AACZ,YAAQ,MAAM,kCAAkC,GAAG;AACnD,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AACF;AAEO,MAAM,UAAU;AAAA,EACrB,SAAS;AAAA,IACP,KAAK;AAAA,MACH,MAAM,CAAC,SAAS;AAAA,MAChB,SAAS;AAAA,MACT,YAAY,CAAC,EAAE,MAAM,MAAM,IAAI,QAAQ,UAAU,MAAM,QAAQ,EAAE,MAAM,UAAU,QAAQ,OAAO,EAAE,CAAC;AAAA,MACnG,aACE;AAAA,IAMJ;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -3,6 +3,8 @@ import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
3
  import { withScopedPayload } from "@open-mercato/shared/lib/api/scoped";
4
4
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
5
  import { BillingAccount } from "../../data/entities.js";
6
+ import { fetchAccountFinancials, fetchOverdueAccountIds, financialsOrZero } from "../../lib/accountFinancials.js";
7
+ const NO_MATCH_ID = "00000000-0000-4000-8000-000000000000";
6
8
  import { billingEntityIds } from "../../data/entityIds.js";
7
9
  import {
8
10
  billingAccountCreateSchema,
@@ -25,6 +27,7 @@ const metadata = routeMetadata;
25
27
  const billingAccountListItemSchema = z.object({
26
28
  id: z.string().uuid(),
27
29
  customer_id: z.string(),
30
+ customer_kind: z.enum(["company", "person"]),
28
31
  name: z.string(),
29
32
  currency_code: z.string(),
30
33
  bill_cycle: z.string(),
@@ -37,6 +40,11 @@ const billingAccountListItemSchema = z.object({
37
40
  last_bill_date: z.string().nullable(),
38
41
  billing_mode: z.string(),
39
42
  credit_limit: z.string(),
43
+ payment_terms_days: z.number().int().nullable(),
44
+ auto_send_invoice: z.boolean(),
45
+ /** Spec F (F8): Σ outstanding of open receivables and its overdue subset (batched per page). */
46
+ balance_due: z.string(),
47
+ overdue_amount: z.string(),
40
48
  is_active: z.boolean(),
41
49
  organization_id: z.string().uuid(),
42
50
  tenant_id: z.string().uuid(),
@@ -59,6 +67,7 @@ const crud = makeCrudRoute({
59
67
  fields: [
60
68
  "id",
61
69
  "customer_id",
70
+ "customer_kind",
62
71
  "name",
63
72
  "currency_code",
64
73
  "bill_cycle",
@@ -71,6 +80,8 @@ const crud = makeCrudRoute({
71
80
  "last_bill_date",
72
81
  "billing_mode",
73
82
  "credit_limit",
83
+ "payment_terms_days",
84
+ "auto_send_invoice",
74
85
  "is_active",
75
86
  "organization_id",
76
87
  "tenant_id",
@@ -83,14 +94,24 @@ const crud = makeCrudRoute({
83
94
  updatedAt: "updated_at",
84
95
  nextBillDate: "next_bill_date"
85
96
  },
86
- buildFilters: async (query) => {
97
+ buildFilters: async (query, ctx) => {
87
98
  const filters = {};
99
+ if (query.hasOverdue === true && ctx.auth?.tenantId) {
100
+ const organizationIds = ctx.organizationIds ?? (ctx.selectedOrganizationId ? [ctx.selectedOrganizationId] : []);
101
+ const em = ctx.container.resolve("em");
102
+ const overdueIds = await fetchOverdueAccountIds(em, { tenantId: ctx.auth.tenantId, organizationIds });
103
+ filters.id = { $in: overdueIds.length > 0 ? overdueIds : [NO_MATCH_ID] };
104
+ }
88
105
  if (typeof query.id === "string" && query.id) {
89
- filters.id = { $eq: query.id };
106
+ const current = filters.id;
107
+ filters.id = current?.$in ? { $in: current.$in.includes(query.id) ? [query.id] : [NO_MATCH_ID] } : { $eq: query.id };
90
108
  }
91
109
  if (typeof query.customerId === "string" && query.customerId) {
92
110
  filters.customer_id = { $eq: query.customerId };
93
111
  }
112
+ if (typeof query.customerKind === "string" && query.customerKind) {
113
+ filters.customer_kind = { $eq: query.customerKind };
114
+ }
94
115
  if (typeof query.currencyCode === "string" && query.currencyCode) {
95
116
  filters.currency_code = { $eq: query.currencyCode.toUpperCase() };
96
117
  }
@@ -110,6 +131,27 @@ const crud = makeCrudRoute({
110
131
  return filters;
111
132
  }
112
133
  },
134
+ hooks: {
135
+ // Spec F (F8): balance due / overdue per row — one GROUP BY for the page.
136
+ afterList: async (payload, ctx) => {
137
+ const items = payload?.items ?? [];
138
+ if (items.length === 0) return;
139
+ const first = items[0];
140
+ const tenantId = typeof first.tenant_id === "string" ? first.tenant_id : null;
141
+ const organizationId = typeof first.organization_id === "string" ? first.organization_id : null;
142
+ const ids = items.map((row) => row.id).filter((id) => typeof id === "string");
143
+ const financials = tenantId && organizationId ? await fetchAccountFinancials(
144
+ ctx.container.resolve("em"),
145
+ { tenantId, organizationId },
146
+ ids
147
+ ) : /* @__PURE__ */ new Map();
148
+ for (const row of items) {
149
+ const values = financialsOrZero(financials, String(row.id));
150
+ row.balance_due = values.balance_due;
151
+ row.overdue_amount = values.overdue_amount;
152
+ }
153
+ }
154
+ },
113
155
  actions: {
114
156
  create: {
115
157
  commandId: "billing.accounts.create",
@@ -165,12 +207,12 @@ const openApi = createBillingCrudOpenApi({
165
207
  create: {
166
208
  schema: billingAccountCreateSchema,
167
209
  responseSchema: defaultCreateResponseSchema,
168
- description: "Creates a new billing account for a customer."
210
+ description: "Creates a new billing account for a customer \u2014 a company or a person (`customerKind`, default `company`). `invoiceLanguage` must be a language supported by the invoice template (`en`, `pl`; default `en`)."
169
211
  },
170
212
  update: {
171
213
  schema: billingAccountUpdateSchema,
172
214
  responseSchema: defaultOkResponseSchema,
173
- description: "Updates a billing account. `currencyCode` and `customerId` are immutable."
215
+ description: "Updates a billing account. `currencyCode`, `customerId` and `customerKind` are immutable."
174
216
  },
175
217
  del: {
176
218
  schema: z.object({ id: z.string().uuid() }),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/billing/api/accounts/route.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { BillingAccount } from '../../data/entities'\nimport { billingEntityIds } from '../../data/entityIds'\nimport {\n billingAccountCreateSchema,\n billingAccountListQuerySchema,\n billingAccountUpdateSchema,\n} from '../../data/validators'\nimport {\n createBillingCrudOpenApi,\n createPagedListResponseSchema,\n defaultCreateResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\n\n// \u2500\u2500\u2500 Auth / RBAC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['billing.account.view'] },\n POST: { requireAuth: true, requireFeatures: ['billing.account.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['billing.account.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['billing.account.manage'] },\n}\n\nexport const metadata = routeMetadata\n\n// \u2500\u2500\u2500 List response item shape (informational \u2014 used by OpenAPI) \u2500\u2500\n\n// The list endpoint projects raw column names \u2014 snake_case.\nconst billingAccountListItemSchema = z.object({\n id: z.string().uuid(),\n customer_id: z.string(),\n name: z.string(),\n currency_code: z.string(),\n bill_cycle: z.string(),\n bill_cycle_anchor: z.string(),\n invoice_email: z.string(),\n invoice_language: z.string(),\n tax_id: z.string().nullable(),\n invoice_address: z.record(z.string(), z.unknown()),\n next_bill_date: z.string(),\n last_bill_date: z.string().nullable(),\n billing_mode: z.string(),\n credit_limit: z.string(),\n is_active: z.boolean(),\n organization_id: z.string().uuid(),\n tenant_id: z.string().uuid(),\n created_at: z.string(),\n updated_at: z.string(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\n// \u2500\u2500\u2500 CRUD route \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: BillingAccount,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: billingAccountListQuerySchema,\n entityId: billingEntityIds.account,\n fields: [\n 'id',\n 'customer_id',\n 'name',\n 'currency_code',\n 'bill_cycle',\n 'bill_cycle_anchor',\n 'invoice_email',\n 'invoice_language',\n 'tax_id',\n 'invoice_address',\n 'next_bill_date',\n 'last_bill_date',\n 'billing_mode',\n 'credit_limit',\n 'is_active',\n 'organization_id',\n 'tenant_id',\n 'created_at',\n 'updated_at',\n ],\n sortFieldMap: {\n name: 'name',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n nextBillDate: 'next_bill_date',\n },\n buildFilters: async (query) => {\n const filters: Record<string, unknown> = {}\n // `?id=<uuid>` narrows the list to one row \u2014 the detail page\n // reads a single account through this filter.\n if (typeof query.id === 'string' && query.id) {\n filters.id = { $eq: query.id }\n }\n if (typeof query.customerId === 'string' && query.customerId) {\n filters.customer_id = { $eq: query.customerId }\n }\n if (typeof query.currencyCode === 'string' && query.currencyCode) {\n filters.currency_code = { $eq: query.currencyCode.toUpperCase() }\n }\n if (typeof query.billCycle === 'string' && query.billCycle) {\n filters.bill_cycle = { $eq: query.billCycle }\n }\n if (typeof query.billingMode === 'string' && query.billingMode) {\n filters.billing_mode = { $eq: query.billingMode }\n }\n if (typeof query.isActive === 'boolean') {\n filters.is_active = { $eq: query.isActive }\n }\n if (typeof query.search === 'string' && query.search.trim()) {\n const pattern = `%${query.search.trim().replace(/[%_]/g, (m) => `\\\\${m}`)}%`\n filters.name = { $ilike: pattern }\n }\n return filters\n },\n },\n actions: {\n create: {\n commandId: 'billing.accounts.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingAccountCreateSchema.parse(scoped)\n },\n response: ({ result }) => ({ id: result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'billing.accounts.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingAccountUpdateSchema.parse(scoped)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'billing.accounts.delete',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n // The factory hands delete a `{ body, query }` envelope \u2014 the\n // id may arrive in either, depending on the caller.\n const envelope = (raw ?? {}) as {\n body?: Record<string, unknown>\n query?: Record<string, unknown>\n }\n const body = envelope.body ?? {}\n const query = envelope.query ?? {}\n const scoped = withScopedPayload(body, ctx, translate)\n const id =\n (typeof body.id === 'string' && body.id) ||\n (typeof query.id === 'string' && query.id) ||\n null\n if (!id) {\n throw new Error('id is required')\n }\n return {\n id,\n tenantId: (scoped as { tenantId?: string }).tenantId,\n organizationId: (scoped as { organizationId?: string }).organizationId,\n }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingAccount',\n pluralName: 'BillingAccounts',\n querySchema: billingAccountListQuerySchema,\n listResponseSchema: createPagedListResponseSchema(billingAccountListItemSchema),\n create: {\n schema: billingAccountCreateSchema,\n responseSchema: defaultCreateResponseSchema,\n description: 'Creates a new billing account for a customer.',\n },\n update: {\n schema: billingAccountUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a billing account. `currencyCode` and `customerId` are immutable.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Soft-deletes a billing account (sets `deleted_at`).',\n },\n})\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AwilixContainer } from 'awilix'\nimport { BillingAccount } from '../../data/entities'\nimport { fetchAccountFinancials, fetchOverdueAccountIds, financialsOrZero } from '../../lib/accountFinancials'\n\n/** A uuid no row has \u2014 narrows an `$in` filter to nothing. */\nconst NO_MATCH_ID = '00000000-0000-4000-8000-000000000000'\nimport { billingEntityIds } from '../../data/entityIds'\nimport {\n billingAccountCreateSchema,\n billingAccountListQuerySchema,\n billingAccountUpdateSchema,\n} from '../../data/validators'\nimport {\n createBillingCrudOpenApi,\n createPagedListResponseSchema,\n defaultCreateResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\n\n// \u2500\u2500\u2500 Auth / RBAC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['billing.account.view'] },\n POST: { requireAuth: true, requireFeatures: ['billing.account.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['billing.account.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['billing.account.manage'] },\n}\n\nexport const metadata = routeMetadata\n\n// \u2500\u2500\u2500 List response item shape (informational \u2014 used by OpenAPI) \u2500\u2500\n\n// The list endpoint projects raw column names \u2014 snake_case.\nconst billingAccountListItemSchema = z.object({\n id: z.string().uuid(),\n customer_id: z.string(),\n customer_kind: z.enum(['company', 'person']),\n name: z.string(),\n currency_code: z.string(),\n bill_cycle: z.string(),\n bill_cycle_anchor: z.string(),\n invoice_email: z.string(),\n invoice_language: z.string(),\n tax_id: z.string().nullable(),\n invoice_address: z.record(z.string(), z.unknown()),\n next_bill_date: z.string(),\n last_bill_date: z.string().nullable(),\n billing_mode: z.string(),\n credit_limit: z.string(),\n payment_terms_days: z.number().int().nullable(),\n auto_send_invoice: z.boolean(),\n /** Spec F (F8): \u03A3 outstanding of open receivables and its overdue subset (batched per page). */\n balance_due: z.string(),\n overdue_amount: z.string(),\n is_active: z.boolean(),\n organization_id: z.string().uuid(),\n tenant_id: z.string().uuid(),\n created_at: z.string(),\n updated_at: z.string(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\n// \u2500\u2500\u2500 CRUD route \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: BillingAccount,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: billingAccountListQuerySchema,\n entityId: billingEntityIds.account,\n fields: [\n 'id',\n 'customer_id',\n 'customer_kind',\n 'name',\n 'currency_code',\n 'bill_cycle',\n 'bill_cycle_anchor',\n 'invoice_email',\n 'invoice_language',\n 'tax_id',\n 'invoice_address',\n 'next_bill_date',\n 'last_bill_date',\n 'billing_mode',\n 'credit_limit',\n 'payment_terms_days',\n 'auto_send_invoice',\n 'is_active',\n 'organization_id',\n 'tenant_id',\n 'created_at',\n 'updated_at',\n ],\n sortFieldMap: {\n name: 'name',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n nextBillDate: 'next_bill_date',\n },\n buildFilters: async (query, ctx) => {\n const filters: Record<string, unknown> = {}\n // Spec F (F8): accounts with an overdue receivable \u2014 one indexed\n // DISTINCT over billing_invoices, intersected with any `id` filter.\n if (query.hasOverdue === true && ctx.auth?.tenantId) {\n const organizationIds = ctx.organizationIds ?? (ctx.selectedOrganizationId ? [ctx.selectedOrganizationId] : [])\n const em = ctx.container.resolve<EntityManager>('em')\n const overdueIds = await fetchOverdueAccountIds(em, { tenantId: ctx.auth.tenantId, organizationIds })\n filters.id = { $in: overdueIds.length > 0 ? overdueIds : [NO_MATCH_ID] }\n }\n // `?id=<uuid>` narrows the list to one row \u2014 the detail page\n // reads a single account through this filter.\n if (typeof query.id === 'string' && query.id) {\n const current = filters.id as { $in?: string[] } | undefined\n filters.id = current?.$in ? { $in: current.$in.includes(query.id) ? [query.id] : [NO_MATCH_ID] } : { $eq: query.id }\n }\n if (typeof query.customerId === 'string' && query.customerId) {\n filters.customer_id = { $eq: query.customerId }\n }\n if (typeof query.customerKind === 'string' && query.customerKind) {\n filters.customer_kind = { $eq: query.customerKind }\n }\n if (typeof query.currencyCode === 'string' && query.currencyCode) {\n filters.currency_code = { $eq: query.currencyCode.toUpperCase() }\n }\n if (typeof query.billCycle === 'string' && query.billCycle) {\n filters.bill_cycle = { $eq: query.billCycle }\n }\n if (typeof query.billingMode === 'string' && query.billingMode) {\n filters.billing_mode = { $eq: query.billingMode }\n }\n if (typeof query.isActive === 'boolean') {\n filters.is_active = { $eq: query.isActive }\n }\n if (typeof query.search === 'string' && query.search.trim()) {\n const pattern = `%${query.search.trim().replace(/[%_]/g, (m) => `\\\\${m}`)}%`\n filters.name = { $ilike: pattern }\n }\n return filters\n },\n },\n hooks: {\n // Spec F (F8): balance due / overdue per row \u2014 one GROUP BY for the page.\n afterList: async (payload: unknown, ctx: unknown) => {\n const items = ((payload as { items?: unknown[] } | null)?.items ?? []) as Array<Record<string, unknown>>\n if (items.length === 0) return\n const first = items[0]\n const tenantId = typeof first.tenant_id === 'string' ? first.tenant_id : null\n const organizationId = typeof first.organization_id === 'string' ? first.organization_id : null\n const ids = items.map((row) => row.id).filter((id): id is string => typeof id === 'string')\n const financials =\n tenantId && organizationId\n ? await fetchAccountFinancials(\n (ctx as { container: AwilixContainer }).container.resolve<EntityManager>('em'),\n { tenantId, organizationId },\n ids,\n )\n : new Map()\n for (const row of items) {\n const values = financialsOrZero(financials, String(row.id))\n row.balance_due = values.balance_due\n row.overdue_amount = values.overdue_amount\n }\n },\n },\n actions: {\n create: {\n commandId: 'billing.accounts.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingAccountCreateSchema.parse(scoped)\n },\n response: ({ result }) => ({ id: result?.id ?? null }),\n status: 201,\n },\n update: {\n commandId: 'billing.accounts.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingAccountUpdateSchema.parse(scoped)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'billing.accounts.delete',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n // The factory hands delete a `{ body, query }` envelope \u2014 the\n // id may arrive in either, depending on the caller.\n const envelope = (raw ?? {}) as {\n body?: Record<string, unknown>\n query?: Record<string, unknown>\n }\n const body = envelope.body ?? {}\n const query = envelope.query ?? {}\n const scoped = withScopedPayload(body, ctx, translate)\n const id =\n (typeof body.id === 'string' && body.id) ||\n (typeof query.id === 'string' && query.id) ||\n null\n if (!id) {\n throw new Error('id is required')\n }\n return {\n id,\n tenantId: (scoped as { tenantId?: string }).tenantId,\n organizationId: (scoped as { organizationId?: string }).organizationId,\n }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingAccount',\n pluralName: 'BillingAccounts',\n querySchema: billingAccountListQuerySchema,\n listResponseSchema: createPagedListResponseSchema(billingAccountListItemSchema),\n create: {\n schema: billingAccountCreateSchema,\n responseSchema: defaultCreateResponseSchema,\n description:\n 'Creates a new billing account for a customer \u2014 a company or a person (`customerKind`, default `company`). ' +\n '`invoiceLanguage` must be a language supported by the invoice template (`en`, `pl`; default `en`).',\n },\n update: {\n schema: billingAccountUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description:\n 'Updates a billing account. `currencyCode`, `customerId` and `customerKind` are immutable.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Soft-deletes a billing account (sets `deleted_at`).',\n },\n})\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -112,15 +112,16 @@ async function GET(req, { params }) {
112
112
  )
113
113
  ]);
114
114
  const invoiceRows = await em.execute(
115
- `SELECT id, invoice_number, status, status_entry_id, issue_date, due_date,
116
- currency_code, subtotal_net_amount, grand_total_net_amount,
117
- grand_total_gross_amount, paid_total_amount, outstanding_amount,
118
- metadata, created_at, updated_at, deleted_at
119
- FROM sales_invoices
115
+ `SELECT id, number, document_type, series, status, test_run, bill_run_id, bill_date,
116
+ period_start, period_end, issue_date, due_date, posted_at, currency_code,
117
+ subtotal_net, tax_total, total_gross, paid_total, credited_total, outstanding,
118
+ corrects_invoice_id, correction_reason, topup_id, buyer_snapshot, seller_snapshot,
119
+ metadata, created_at, updated_at
120
+ FROM billing_invoices
120
121
  WHERE tenant_id = ?
121
122
  AND organization_id = ?
122
123
  AND deleted_at IS NULL
123
- AND metadata->>'bill_account_id' = ?
124
+ AND bill_account_id = ?
124
125
  AND created_at >= ?
125
126
  AND created_at <= ?
126
127
  ORDER BY created_at DESC
@@ -140,7 +141,10 @@ async function GET(req, { params }) {
140
141
  return NextResponse.json({
141
142
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
142
143
  // v2 adds prepaid balance / transactions / top-ups / statements (SPEC-002).
143
- schemaVersion: 2,
144
+ // v3 (Spec 0): invoices come from billing_invoices with billing field names.
145
+ // v4 (Spec B): items carry taxRate; the account carries paymentTermsDays /
146
+ // autoSendInvoice; invoice metadata may carry draft_number / deliveries.
147
+ schemaVersion: 4,
144
148
  tenantId,
145
149
  organizationId,
146
150
  window: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/billing/api/export/account/%5Bid%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport {\n BillingAccount,\n BillingAccountBalance,\n BillingAccountTransaction,\n BillingAccountUsage,\n BillingItem,\n BillingStatement,\n BillingTopup,\n BillingRunOutcome,\n BillingDraftInvoiceEdit,\n} from '../../../../data/entities'\n\n/**\n * `GET /api/billing/export/account/[id]` \u2014 GDPR portability dump.\n *\n * Returns a full JSON serialization of a single Billing Account\n * including:\n * - The account row itself.\n * - Every active + soft-deleted `BillingItem` for the account.\n * - Every `BillingAccountUsage` record.\n * - Every `BillingRunOutcome` row referencing the account.\n * - Every `core/sales` invoice the engine emitted for the account\n * (matched via `metadata->>'bill_account_id'`).\n * - Every `BillingDraftInvoiceEdit` audit row for those invoices.\n *\n * Scope: the caller's auth tenant/organization is the authority \u2014 a\n * 404 is returned even for valid UUIDs that belong to a different\n * tenant (no cross-tenant leak).\n *\n * Soft-deleted rows are included on purpose: GDPR portability is\n * about the data we still hold, not just the data the UI shows.\n */\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['billing.account.view'] },\n}\n\nexport async function GET(\n req: Request,\n { params }: { params: Promise<{ id: string }> },\n) {\n let container: Awaited<ReturnType<typeof createRequestContainer>> | null = null\n try {\n container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const tenantId = auth.tenantId\n const organizationId = (auth as { orgId?: string }).orgId ?? null\n if (!organizationId) {\n return NextResponse.json(\n { error: 'Organization scope required' },\n { status: 403 },\n )\n }\n\n const { id: accountId } = await params\n\n const em = (container.resolve('em') as EntityManager).fork()\n const account = await em.findOne(BillingAccount, {\n id: accountId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (!account) {\n return NextResponse.json({ error: 'Account not found' }, { status: 404 })\n }\n\n // Time-window cap for usage records, run outcomes and invoices.\n //\n // Scale note: a telco account can carry millions of usage rows.\n // Returning everything would OOM on the export path. We default\n // to the last 12 months and let the caller override via `since` /\n // `until` for full GDPR-compliance dumps (a regulator request that\n // legitimately needs the lifetime export should set `since=1970-01-01`).\n // Each query is independently capped by `EXPORT_HARD_LIMIT` so a\n // single year of pathological volume still returns rather than\n // hanging.\n const url = new URL(req.url)\n const since = parseDate(url.searchParams.get('since'))\n ?? defaultSince()\n const until = parseDate(url.searchParams.get('until')) ?? new Date()\n\n const [items, usage, outcomes, balance, transactions, topups, statements] = await Promise.all([\n // Items are bounded by integration volume (typically <100 per\n // account); no time-window needed \u2014 they often outlive a single\n // export window and the operator needs to see active items\n // regardless of when they were created.\n em.find(\n BillingItem,\n { tenantId, organizationId, billAccountId: account.id } as never,\n { limit: EXPORT_HARD_LIMIT },\n ),\n em.find(\n BillingAccountUsage,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n periodEnd: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { periodEnd: 'DESC' } },\n ),\n em.find(\n BillingRunOutcome,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n createdAt: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n ),\n // SPEC-002 prepaid data \u2014 the balance row + the windowed money trail.\n em.findOne(BillingAccountBalance, {\n tenantId,\n billAccountId: account.id,\n } as never),\n em.find(\n BillingAccountTransaction,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n createdAt: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n ),\n em.find(\n BillingTopup,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n createdAt: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n ),\n em.find(\n BillingStatement,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n periodEnd: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { periodEnd: 'DESC' } },\n ),\n ])\n\n // Invoices created in the window. The `metadata->>'bill_account_id'`\n // predicate keeps non-billing core/sales invoices out.\n const invoiceRows = (await em.execute(\n `SELECT id, invoice_number, status, status_entry_id, issue_date, due_date,\n currency_code, subtotal_net_amount, grand_total_net_amount,\n grand_total_gross_amount, paid_total_amount, outstanding_amount,\n metadata, created_at, updated_at, deleted_at\n FROM sales_invoices\n WHERE tenant_id = ?\n AND organization_id = ?\n AND deleted_at IS NULL\n AND metadata->>'bill_account_id' = ?\n AND created_at >= ?\n AND created_at <= ?\n ORDER BY created_at DESC\n LIMIT ?`,\n [tenantId, organizationId, account.id, since, until, EXPORT_HARD_LIMIT],\n )) as unknown as Array<{ id: string }>\n\n const invoiceIds = invoiceRows.map((r) => r.id)\n const draftEdits =\n invoiceIds.length > 0\n ? await em.find(\n BillingDraftInvoiceEdit,\n {\n tenantId,\n organizationId,\n invoiceId: { $in: invoiceIds },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n )\n : []\n\n return NextResponse.json({\n exportedAt: new Date().toISOString(),\n // v2 adds prepaid balance / transactions / top-ups / statements (SPEC-002).\n schemaVersion: 2,\n tenantId,\n organizationId,\n window: {\n since: since.toISOString(),\n until: until.toISOString(),\n hardLimit: EXPORT_HARD_LIMIT,\n },\n account,\n items,\n usage,\n runOutcomes: outcomes,\n invoices: invoiceRows,\n draftInvoiceEdits: draftEdits,\n // SPEC-002 prepaid section.\n balance,\n transactions,\n topups,\n statements,\n })\n } catch (err) {\n console.error('[billing/export/account.GET]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n\n/**\n * Per-collection cap to keep a single export bounded in size and time\n * even when the caller asks for `since=1970-01-01`. The full GDPR\n * lifetime dump for an outlier account requires a follow-up\n * background-job approach (chunked NDJSON to object storage) \u2014 out of\n * scope for the v1 admin export endpoint.\n */\nconst EXPORT_HARD_LIMIT = 50_000\n\n/**\n * Default time window for the export \u2014 last 12 months. Operators\n * pulling for routine GDPR requests almost always want a finite\n * window; the regulator-compliance \"lifetime\" case overrides via\n * `?since=1970-01-01`.\n */\nfunction defaultSince(): Date {\n const d = new Date()\n d.setUTCFullYear(d.getUTCFullYear() - 1)\n return d\n}\n\nfunction parseDate(value: string | null | undefined): Date | null {\n if (!value) return null\n const d = new Date(value)\n return Number.isNaN(d.getTime()) ? null : d\n}\n\nexport const openApi = {\n methods: {\n GET: {\n tags: ['Billing'],\n summary: 'Export a billing account (GDPR portability)',\n parameters: [\n {\n name: 'id',\n in: 'path',\n required: true,\n schema: { type: 'string', format: 'uuid' },\n },\n {\n name: 'since',\n in: 'query',\n required: false,\n description:\n 'ISO timestamp; lower bound for usage / outcomes / invoices windows. Defaults to today \u2212 12 months.',\n schema: { type: 'string', format: 'date-time' },\n },\n {\n name: 'until',\n in: 'query',\n required: false,\n description: 'ISO timestamp; upper bound. Defaults to now.',\n schema: { type: 'string', format: 'date-time' },\n },\n ],\n description:\n \"Returns a JSON dump of a single Billing Account: the account itself, every Billing Item (no time cap \u2014 items often outlive a single export window), and the Usage records / BillRunOutcomes / emitted core/sales invoices / BillingDraftInvoiceEdit rows whose timestamps fall in the [since, until] window (defaults to the last 12 months). Each collection is independently capped by EXPORT_HARD_LIMIT = 50,000 rows. Lifetime dumps for outlier accounts need a follow-up chunked background-job approach \u2014 out of scope for this v1 endpoint.\",\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport {\n BillingAccount,\n BillingAccountBalance,\n BillingAccountTransaction,\n BillingAccountUsage,\n BillingItem,\n BillingStatement,\n BillingTopup,\n BillingRunOutcome,\n BillingDraftInvoiceEdit,\n} from '../../../../data/entities'\n\n/**\n * `GET /api/billing/export/account/[id]` \u2014 GDPR portability dump.\n *\n * Returns a full JSON serialization of a single Billing Account\n * including:\n * - The account row itself.\n * - Every active + soft-deleted `BillingItem` for the account.\n * - Every `BillingAccountUsage` record.\n * - Every `BillingRunOutcome` row referencing the account.\n * - Every billing document (`billing_invoices`) of the account.\n * - Every `BillingDraftInvoiceEdit` audit row for those invoices.\n *\n * Scope: the caller's auth tenant/organization is the authority \u2014 a\n * 404 is returned even for valid UUIDs that belong to a different\n * tenant (no cross-tenant leak).\n *\n * Soft-deleted rows are included on purpose: GDPR portability is\n * about the data we still hold, not just the data the UI shows.\n */\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['billing.account.view'] },\n}\n\nexport async function GET(\n req: Request,\n { params }: { params: Promise<{ id: string }> },\n) {\n let container: Awaited<ReturnType<typeof createRequestContainer>> | null = null\n try {\n container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const tenantId = auth.tenantId\n const organizationId = (auth as { orgId?: string }).orgId ?? null\n if (!organizationId) {\n return NextResponse.json(\n { error: 'Organization scope required' },\n { status: 403 },\n )\n }\n\n const { id: accountId } = await params\n\n const em = (container.resolve('em') as EntityManager).fork()\n const account = await em.findOne(BillingAccount, {\n id: accountId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (!account) {\n return NextResponse.json({ error: 'Account not found' }, { status: 404 })\n }\n\n // Time-window cap for usage records, run outcomes and invoices.\n //\n // Scale note: a telco account can carry millions of usage rows.\n // Returning everything would OOM on the export path. We default\n // to the last 12 months and let the caller override via `since` /\n // `until` for full GDPR-compliance dumps (a regulator request that\n // legitimately needs the lifetime export should set `since=1970-01-01`).\n // Each query is independently capped by `EXPORT_HARD_LIMIT` so a\n // single year of pathological volume still returns rather than\n // hanging.\n const url = new URL(req.url)\n const since = parseDate(url.searchParams.get('since'))\n ?? defaultSince()\n const until = parseDate(url.searchParams.get('until')) ?? new Date()\n\n const [items, usage, outcomes, balance, transactions, topups, statements] = await Promise.all([\n // Items are bounded by integration volume (typically <100 per\n // account); no time-window needed \u2014 they often outlive a single\n // export window and the operator needs to see active items\n // regardless of when they were created.\n em.find(\n BillingItem,\n { tenantId, organizationId, billAccountId: account.id } as never,\n { limit: EXPORT_HARD_LIMIT },\n ),\n em.find(\n BillingAccountUsage,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n periodEnd: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { periodEnd: 'DESC' } },\n ),\n em.find(\n BillingRunOutcome,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n createdAt: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n ),\n // SPEC-002 prepaid data \u2014 the balance row + the windowed money trail.\n em.findOne(BillingAccountBalance, {\n tenantId,\n billAccountId: account.id,\n } as never),\n em.find(\n BillingAccountTransaction,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n createdAt: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n ),\n em.find(\n BillingTopup,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n createdAt: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n ),\n em.find(\n BillingStatement,\n {\n tenantId,\n organizationId,\n billAccountId: account.id,\n periodEnd: { $gte: since, $lte: until },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { periodEnd: 'DESC' } },\n ),\n ])\n\n // Billing documents created in the window (Spec 0: billing_invoices).\n const invoiceRows = (await em.execute(\n `SELECT id, number, document_type, series, status, test_run, bill_run_id, bill_date,\n period_start, period_end, issue_date, due_date, posted_at, currency_code,\n subtotal_net, tax_total, total_gross, paid_total, credited_total, outstanding,\n corrects_invoice_id, correction_reason, topup_id, buyer_snapshot, seller_snapshot,\n metadata, created_at, updated_at\n FROM billing_invoices\n WHERE tenant_id = ?\n AND organization_id = ?\n AND deleted_at IS NULL\n AND bill_account_id = ?\n AND created_at >= ?\n AND created_at <= ?\n ORDER BY created_at DESC\n LIMIT ?`,\n [tenantId, organizationId, account.id, since, until, EXPORT_HARD_LIMIT],\n )) as unknown as Array<{ id: string }>\n\n const invoiceIds = invoiceRows.map((r) => r.id)\n const draftEdits =\n invoiceIds.length > 0\n ? await em.find(\n BillingDraftInvoiceEdit,\n {\n tenantId,\n organizationId,\n invoiceId: { $in: invoiceIds },\n } as never,\n { limit: EXPORT_HARD_LIMIT, orderBy: { createdAt: 'DESC' } },\n )\n : []\n\n return NextResponse.json({\n exportedAt: new Date().toISOString(),\n // v2 adds prepaid balance / transactions / top-ups / statements (SPEC-002).\n // v3 (Spec 0): invoices come from billing_invoices with billing field names.\n // v4 (Spec B): items carry taxRate; the account carries paymentTermsDays /\n // autoSendInvoice; invoice metadata may carry draft_number / deliveries.\n schemaVersion: 4,\n tenantId,\n organizationId,\n window: {\n since: since.toISOString(),\n until: until.toISOString(),\n hardLimit: EXPORT_HARD_LIMIT,\n },\n account,\n items,\n usage,\n runOutcomes: outcomes,\n invoices: invoiceRows,\n draftInvoiceEdits: draftEdits,\n // SPEC-002 prepaid section.\n balance,\n transactions,\n topups,\n statements,\n })\n } catch (err) {\n console.error('[billing/export/account.GET]', err)\n return NextResponse.json({ error: 'Internal error' }, { status: 500 })\n }\n}\n\n/**\n * Per-collection cap to keep a single export bounded in size and time\n * even when the caller asks for `since=1970-01-01`. The full GDPR\n * lifetime dump for an outlier account requires a follow-up\n * background-job approach (chunked NDJSON to object storage) \u2014 out of\n * scope for the v1 admin export endpoint.\n */\nconst EXPORT_HARD_LIMIT = 50_000\n\n/**\n * Default time window for the export \u2014 last 12 months. Operators\n * pulling for routine GDPR requests almost always want a finite\n * window; the regulator-compliance \"lifetime\" case overrides via\n * `?since=1970-01-01`.\n */\nfunction defaultSince(): Date {\n const d = new Date()\n d.setUTCFullYear(d.getUTCFullYear() - 1)\n return d\n}\n\nfunction parseDate(value: string | null | undefined): Date | null {\n if (!value) return null\n const d = new Date(value)\n return Number.isNaN(d.getTime()) ? null : d\n}\n\nexport const openApi = {\n methods: {\n GET: {\n tags: ['Billing'],\n summary: 'Export a billing account (GDPR portability)',\n parameters: [\n {\n name: 'id',\n in: 'path',\n required: true,\n schema: { type: 'string', format: 'uuid' },\n },\n {\n name: 'since',\n in: 'query',\n required: false,\n description:\n 'ISO timestamp; lower bound for usage / outcomes / invoices windows. Defaults to today \u2212 12 months.',\n schema: { type: 'string', format: 'date-time' },\n },\n {\n name: 'until',\n in: 'query',\n required: false,\n description: 'ISO timestamp; upper bound. Defaults to now.',\n schema: { type: 'string', format: 'date-time' },\n },\n ],\n description:\n \"Returns a JSON dump of a single Billing Account: the account itself, every Billing Item (no time cap \u2014 items often outlive a single export window), and the Usage records / BillRunOutcomes / emitted core/sales invoices / BillingDraftInvoiceEdit rows whose timestamps fall in the [since, until] window (defaults to the last 12 months). Each collection is independently capped by EXPORT_HARD_LIMIT = 50,000 rows. Lifetime dumps for outlier accounts need a follow-up chunked background-job approach \u2014 out of scope for this v1 endpoint.\",\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import { readRequestBody, runCommandRoute } from "../../../../lib/commandRoute.js";
3
+ const metadata = {
4
+ POST: { requireAuth: true, requireFeatures: ["billing.invoice.correct"] }
5
+ };
6
+ const lineSchema = z.object({
7
+ correctsLineId: z.string().uuid().optional(),
8
+ description: z.string().trim().min(1).max(500),
9
+ quantity: z.coerce.number().positive(),
10
+ unitPriceNet: z.coerce.number().nonnegative(),
11
+ taxRate: z.coerce.number().min(0).max(100).optional()
12
+ });
13
+ const bodySchema = z.object({
14
+ invoiceId: z.string().uuid(),
15
+ type: z.enum(["credit_memo", "debit_memo"]),
16
+ reason: z.string().trim().min(1).max(1e3),
17
+ lines: z.array(lineSchema).max(200).optional()
18
+ });
19
+ async function POST(req, { params }) {
20
+ const { id } = await params;
21
+ const raw = await readRequestBody(req);
22
+ return runCommandRoute(
23
+ req,
24
+ {
25
+ commandId: "billing.invoices.create_memo",
26
+ bodySchema,
27
+ resourceKind: "billing.invoice",
28
+ resourceId: (body) => body.invoiceId,
29
+ logLabel: "billing/invoices/[id]/memos.POST"
30
+ },
31
+ { ...raw && typeof raw === "object" ? raw : {}, invoiceId: id }
32
+ );
33
+ }
34
+ const openApi = {
35
+ methods: {
36
+ POST: {
37
+ tags: ["Billing"],
38
+ summary: "Create a credit or debit memo",
39
+ description: "Creates a draft memo for a posted / partially paid / paid invoice (a fully credited invoice accepts debit memos only). `reason` is required and printed. Credit memo without `lines` = full credit of every original line; lines with `correctsLineId` keep the original VAT rate; new lines default to the organization rate. Amounts are positive \u2014 the document type gives the sign.",
40
+ requestBody: { schema: bodySchema.omit({ invoiceId: true }) },
41
+ responses: [{ status: 200, description: "`{ memoId, number, documentType, totalGross }`" }],
42
+ errors: [
43
+ { status: 409, description: "`billing.memo.invalid_original` / `billing.memo.exceeds_invoice`" },
44
+ { status: 400, description: "`billing.memo.invalid_line`" }
45
+ ]
46
+ }
47
+ }
48
+ };
49
+ export {
50
+ POST,
51
+ metadata,
52
+ openApi
53
+ };
54
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/billing/api/invoices/%5Bid%5D/memos/route.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { readRequestBody, runCommandRoute } from '../../../../lib/commandRoute'\n\n/**\n * `POST /api/billing/invoices/[id]/memos` \u2014 create a DRAFT credit or debit\n * memo correcting a posted billing invoice (Spec D). Post it with\n * `POST /api/billing/invoices/post`.\n */\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.invoice.correct'] },\n}\n\nconst lineSchema = z.object({\n correctsLineId: z.string().uuid().optional(),\n description: z.string().trim().min(1).max(500),\n quantity: z.coerce.number().positive(),\n unitPriceNet: z.coerce.number().nonnegative(),\n taxRate: z.coerce.number().min(0).max(100).optional(),\n})\n\nconst bodySchema = z.object({\n invoiceId: z.string().uuid(),\n type: z.enum(['credit_memo', 'debit_memo']),\n reason: z.string().trim().min(1).max(1000),\n lines: z.array(lineSchema).max(200).optional(),\n})\n\nexport async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {\n const { id } = await params\n const raw = (await readRequestBody(req)) as Record<string, unknown>\n return runCommandRoute(\n req,\n {\n commandId: 'billing.invoices.create_memo',\n bodySchema,\n resourceKind: 'billing.invoice',\n resourceId: (body) => body.invoiceId,\n logLabel: 'billing/invoices/[id]/memos.POST',\n },\n { ...(raw && typeof raw === 'object' ? raw : {}), invoiceId: id },\n )\n}\n\nexport const openApi = {\n methods: {\n POST: {\n tags: ['Billing'],\n summary: 'Create a credit or debit memo',\n description:\n 'Creates a draft memo for a posted / partially paid / paid invoice (a fully credited invoice ' +\n 'accepts debit memos only). `reason` is required and printed. Credit memo without `lines` = full ' +\n 'credit of every original line; lines with `correctsLineId` keep the original VAT rate; new lines ' +\n 'default to the organization rate. Amounts are positive \u2014 the document type gives the sign.',\n requestBody: { schema: bodySchema.omit({ invoiceId: true }) },\n responses: [{ status: 200, description: '`{ memoId, number, documentType, totalGross }`' }],\n errors: [\n { status: 409, description: '`billing.memo.invalid_original` / `billing.memo.exceeds_invoice`' },\n { status: 400, description: '`billing.memo.invalid_line`' },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,iBAAiB,uBAAuB;AAQ1C,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAC1E;AAEA,MAAM,aAAa,EAAE,OAAO;AAAA,EAC1B,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;AAAA,EAC7C,UAAU,EAAE,OAAO,OAAO,EAAE,SAAS;AAAA,EACrC,cAAc,EAAE,OAAO,OAAO,EAAE,YAAY;AAAA,EAC5C,SAAS,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AACtD,CAAC;AAED,MAAM,aAAa,EAAE,OAAO;AAAA,EAC1B,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,MAAM,EAAE,KAAK,CAAC,eAAe,YAAY,CAAC;AAAA,EAC1C,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAAA,EACzC,OAAO,EAAE,MAAM,UAAU,EAAE,IAAI,GAAG,EAAE,SAAS;AAC/C,CAAC;AAED,eAAsB,KAAK,KAAc,EAAE,OAAO,GAAwC;AACxF,QAAM,EAAE,GAAG,IAAI,MAAM;AACrB,QAAM,MAAO,MAAM,gBAAgB,GAAG;AACtC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX;AAAA,MACA,cAAc;AAAA,MACd,YAAY,CAAC,SAAS,KAAK;AAAA,MAC3B,UAAU;AAAA,IACZ;AAAA,IACA,EAAE,GAAI,OAAO,OAAO,QAAQ,WAAW,MAAM,CAAC,GAAI,WAAW,GAAG;AAAA,EAClE;AACF;AAEO,MAAM,UAAU;AAAA,EACrB,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,MAAM,CAAC,SAAS;AAAA,MAChB,SAAS;AAAA,MACT,aACE;AAAA,MAIF,aAAa,EAAE,QAAQ,WAAW,KAAK,EAAE,WAAW,KAAK,CAAC,EAAE;AAAA,MAC5D,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,iDAAiD,CAAC;AAAA,MAC1F,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,mEAAmE;AAAA,QAC/F,EAAE,QAAQ,KAAK,aAAa,8BAA8B;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }