@dainamite/billing 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (614) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/README.md +14 -0
  3. package/dist/modules/billing/acl.js +13 -1
  4. package/dist/modules/billing/acl.js.map +2 -2
  5. package/dist/modules/billing/api/accounts/[id]/summary/route.js +52 -0
  6. package/dist/modules/billing/api/accounts/[id]/summary/route.js.map +7 -0
  7. package/dist/modules/billing/api/accounts/route.js +46 -4
  8. package/dist/modules/billing/api/accounts/route.js.map +2 -2
  9. package/dist/modules/billing/api/export/account/[id]/route.js +16 -12
  10. package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
  11. package/dist/modules/billing/api/invoices/[id]/memos/route.js +54 -0
  12. package/dist/modules/billing/api/invoices/[id]/memos/route.js.map +7 -0
  13. package/dist/modules/billing/api/invoices/[id]/pdf/route.js +69 -0
  14. package/dist/modules/billing/api/invoices/[id]/pdf/route.js.map +7 -0
  15. package/dist/modules/billing/api/invoices/[id]/route.js +44 -24
  16. package/dist/modules/billing/api/invoices/[id]/route.js.map +2 -2
  17. package/dist/modules/billing/api/invoices/[id]/send/route.js +107 -0
  18. package/dist/modules/billing/api/invoices/[id]/send/route.js.map +7 -0
  19. package/dist/modules/billing/api/invoices/add-line/route.js +3 -3
  20. package/dist/modules/billing/api/invoices/add-line/route.js.map +2 -2
  21. package/dist/modules/billing/api/invoices/delete-draft/route.js +33 -0
  22. package/dist/modules/billing/api/invoices/delete-draft/route.js.map +7 -0
  23. package/dist/modules/billing/api/invoices/edit-line/route.js +3 -3
  24. package/dist/modules/billing/api/invoices/edit-line/route.js.map +2 -2
  25. package/dist/modules/billing/api/invoices/post/route.js +2 -2
  26. package/dist/modules/billing/api/invoices/post/route.js.map +2 -2
  27. package/dist/modules/billing/api/invoices/remove-line/route.js +3 -3
  28. package/dist/modules/billing/api/invoices/remove-line/route.js.map +2 -2
  29. package/dist/modules/billing/api/invoices/route.js +45 -46
  30. package/dist/modules/billing/api/invoices/route.js.map +2 -2
  31. package/dist/modules/billing/api/items/bulk/route.js +1 -1
  32. package/dist/modules/billing/api/items/bulk/route.js.map +2 -2
  33. package/dist/modules/billing/api/items/change-price/route.js +44 -0
  34. package/dist/modules/billing/api/items/change-price/route.js.map +7 -0
  35. package/dist/modules/billing/api/items/route.js +19 -7
  36. package/dist/modules/billing/api/items/route.js.map +2 -2
  37. package/dist/modules/billing/api/payment-imports/[id]/route.js +49 -0
  38. package/dist/modules/billing/api/payment-imports/[id]/route.js.map +7 -0
  39. package/dist/modules/billing/api/payment-imports/route.js +57 -0
  40. package/dist/modules/billing/api/payment-imports/route.js.map +7 -0
  41. package/dist/modules/billing/api/payments/[id]/route.js +60 -0
  42. package/dist/modules/billing/api/payments/[id]/route.js.map +7 -0
  43. package/dist/modules/billing/api/payments/allocate/route.js +35 -0
  44. package/dist/modules/billing/api/payments/allocate/route.js.map +7 -0
  45. package/dist/modules/billing/api/payments/assign/route.js +36 -0
  46. package/dist/modules/billing/api/payments/assign/route.js.map +7 -0
  47. package/dist/modules/billing/api/payments/convert-to-topup/route.js +35 -0
  48. package/dist/modules/billing/api/payments/convert-to-topup/route.js.map +7 -0
  49. package/dist/modules/billing/api/payments/ignore/route.js +35 -0
  50. package/dist/modules/billing/api/payments/ignore/route.js.map +7 -0
  51. package/dist/modules/billing/api/payments/import/route.js +63 -0
  52. package/dist/modules/billing/api/payments/import/route.js.map +7 -0
  53. package/dist/modules/billing/api/payments/restore/route.js +35 -0
  54. package/dist/modules/billing/api/payments/restore/route.js.map +7 -0
  55. package/dist/modules/billing/api/payments/route.js +114 -0
  56. package/dist/modules/billing/api/payments/route.js.map +7 -0
  57. package/dist/modules/billing/api/payments/unallocate/route.js +35 -0
  58. package/dist/modules/billing/api/payments/unallocate/route.js.map +7 -0
  59. package/dist/modules/billing/api/payments/unassign/route.js +35 -0
  60. package/dist/modules/billing/api/payments/unassign/route.js.map +7 -0
  61. package/dist/modules/billing/api/run-outcomes/route.js +44 -7
  62. package/dist/modules/billing/api/run-outcomes/route.js.map +2 -2
  63. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js +61 -0
  64. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js.map +7 -0
  65. package/dist/modules/billing/api/runs/[id]/route.js +53 -0
  66. package/dist/modules/billing/api/runs/[id]/route.js.map +7 -0
  67. package/dist/modules/billing/api/runs/retry-failed/route.js +3 -3
  68. package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
  69. package/dist/modules/billing/api/runs/route.js +11 -5
  70. package/dist/modules/billing/api/runs/route.js.map +2 -2
  71. package/dist/modules/billing/api/settings/number-preview/route.js +42 -0
  72. package/dist/modules/billing/api/settings/number-preview/route.js.map +7 -0
  73. package/dist/modules/billing/api/settings/route.js +92 -0
  74. package/dist/modules/billing/api/settings/route.js.map +7 -0
  75. package/dist/modules/billing/api/statements/route.js +3 -0
  76. package/dist/modules/billing/api/statements/route.js.map +2 -2
  77. package/dist/modules/billing/api/test-invoices/wipe/route.js +2 -2
  78. package/dist/modules/billing/api/test-invoices/wipe/route.js.map +2 -2
  79. package/dist/modules/billing/backend/billing/accounts/[id]/page.js +121 -232
  80. package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
  81. package/dist/modules/billing/backend/billing/accounts/create/page.js +6 -0
  82. package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
  83. package/dist/modules/billing/backend/billing/accounts/page.js +63 -4
  84. package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
  85. package/dist/modules/billing/backend/billing/invoices/[id]/page.js +297 -65
  86. package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +3 -3
  87. package/dist/modules/billing/backend/billing/invoices/page.js +6 -172
  88. package/dist/modules/billing/backend/billing/invoices/page.js.map +2 -2
  89. package/dist/modules/billing/backend/billing/items/[id]/page.js +51 -7
  90. package/dist/modules/billing/backend/billing/items/[id]/page.js.map +2 -2
  91. package/dist/modules/billing/backend/billing/items/create/page.js +9 -6
  92. package/dist/modules/billing/backend/billing/items/create/page.js.map +2 -2
  93. package/dist/modules/billing/backend/billing/items/page.js +8 -2
  94. package/dist/modules/billing/backend/billing/items/page.js.map +2 -2
  95. package/dist/modules/billing/backend/billing/payments/[id]/page.js +297 -0
  96. package/dist/modules/billing/backend/billing/payments/[id]/page.js.map +7 -0
  97. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js +21 -0
  98. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js.map +7 -0
  99. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js +74 -0
  100. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js.map +7 -0
  101. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js +21 -0
  102. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js.map +7 -0
  103. package/dist/modules/billing/backend/billing/payments/page.js +58 -0
  104. package/dist/modules/billing/backend/billing/payments/page.js.map +7 -0
  105. package/dist/modules/billing/backend/billing/payments/page.meta.js +18 -0
  106. package/dist/modules/billing/backend/billing/payments/page.meta.js.map +7 -0
  107. package/dist/modules/billing/backend/billing/runs/[id]/page.js +49 -14
  108. package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +2 -2
  109. package/dist/modules/billing/backend/billing/runs/page.js +58 -7
  110. package/dist/modules/billing/backend/billing/runs/page.js.map +2 -2
  111. package/dist/modules/billing/backend/billing/statements/[id]/page.js +61 -6
  112. package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +2 -2
  113. package/dist/modules/billing/backend/billing/statements/page.js +1 -1
  114. package/dist/modules/billing/backend/billing/statements/page.js.map +2 -2
  115. package/dist/modules/billing/backend/billing/topups/page.js +1 -1
  116. package/dist/modules/billing/backend/billing/topups/page.js.map +2 -2
  117. package/dist/modules/billing/backend/billing/transactions/page.js +1 -1
  118. package/dist/modules/billing/backend/billing/transactions/page.js.map +2 -2
  119. package/dist/modules/billing/backend/billing/usage/create/page.js +3 -2
  120. package/dist/modules/billing/backend/billing/usage/create/page.js.map +2 -2
  121. package/dist/modules/billing/backend/config/billing/page.js +472 -0
  122. package/dist/modules/billing/backend/config/billing/page.js.map +7 -0
  123. package/dist/modules/billing/backend/config/billing/page.meta.js +19 -0
  124. package/dist/modules/billing/backend/config/billing/page.meta.js.map +7 -0
  125. package/dist/modules/billing/ce.js.map +2 -2
  126. package/dist/modules/billing/cli.js +22 -1
  127. package/dist/modules/billing/cli.js.map +2 -2
  128. package/dist/modules/billing/commands/accounts.js +6 -0
  129. package/dist/modules/billing/commands/accounts.js.map +2 -2
  130. package/dist/modules/billing/commands/index.js +3 -0
  131. package/dist/modules/billing/commands/index.js.map +2 -2
  132. package/dist/modules/billing/commands/invoices.js +272 -170
  133. package/dist/modules/billing/commands/invoices.js.map +2 -2
  134. package/dist/modules/billing/commands/items.js +108 -0
  135. package/dist/modules/billing/commands/items.js.map +2 -2
  136. package/dist/modules/billing/commands/memos.js +179 -0
  137. package/dist/modules/billing/commands/memos.js.map +7 -0
  138. package/dist/modules/billing/commands/payments.js +379 -0
  139. package/dist/modules/billing/commands/payments.js.map +7 -0
  140. package/dist/modules/billing/commands/runs.js +154 -7
  141. package/dist/modules/billing/commands/runs.js.map +2 -2
  142. package/dist/modules/billing/commands/settings.js +59 -0
  143. package/dist/modules/billing/commands/settings.js.map +7 -0
  144. package/dist/modules/billing/commands/topups.js +9 -2
  145. package/dist/modules/billing/commands/topups.js.map +2 -2
  146. package/dist/modules/billing/commands/transactions.js +2 -2
  147. package/dist/modules/billing/commands/transactions.js.map +2 -2
  148. package/dist/modules/billing/components/AccountForm.js +149 -42
  149. package/dist/modules/billing/components/AccountForm.js.map +2 -2
  150. package/dist/modules/billing/components/AccountInvoicesSection.js +64 -0
  151. package/dist/modules/billing/components/AccountInvoicesSection.js.map +7 -0
  152. package/dist/modules/billing/components/AccountPaymentsSection.js +43 -0
  153. package/dist/modules/billing/components/AccountPaymentsSection.js.map +7 -0
  154. package/dist/modules/billing/components/AccountPicker.js +56 -0
  155. package/dist/modules/billing/components/AccountPicker.js.map +7 -0
  156. package/dist/modules/billing/components/AllocatePaymentDialog.js +134 -0
  157. package/dist/modules/billing/components/AllocatePaymentDialog.js.map +7 -0
  158. package/dist/modules/billing/components/BillingDataTable.js +21 -0
  159. package/dist/modules/billing/components/BillingDataTable.js.map +7 -0
  160. package/dist/modules/billing/components/BillingSettingsLink.js +19 -0
  161. package/dist/modules/billing/components/BillingSettingsLink.js.map +7 -0
  162. package/dist/modules/billing/components/ChangeItemPriceDialog.js +158 -0
  163. package/dist/modules/billing/components/ChangeItemPriceDialog.js.map +7 -0
  164. package/dist/modules/billing/components/CreateMemoDialog.js +200 -0
  165. package/dist/modules/billing/components/CreateMemoDialog.js.map +7 -0
  166. package/dist/modules/billing/components/CustomerLabel.js +33 -0
  167. package/dist/modules/billing/components/CustomerLabel.js.map +7 -0
  168. package/dist/modules/billing/components/InvoicesTable.js +270 -0
  169. package/dist/modules/billing/components/InvoicesTable.js.map +7 -0
  170. package/dist/modules/billing/components/ItemForm.js +176 -44
  171. package/dist/modules/billing/components/ItemForm.js.map +2 -2
  172. package/dist/modules/billing/components/ItemPriceField.js +46 -0
  173. package/dist/modules/billing/components/ItemPriceField.js.map +7 -0
  174. package/dist/modules/billing/components/LineFormDialog.js +25 -2
  175. package/dist/modules/billing/components/LineFormDialog.js.map +2 -2
  176. package/dist/modules/billing/components/ManualPaymentDialog.js +122 -0
  177. package/dist/modules/billing/components/ManualPaymentDialog.js.map +7 -0
  178. package/dist/modules/billing/components/MoneyInput.js +14 -0
  179. package/dist/modules/billing/components/MoneyInput.js.map +7 -0
  180. package/dist/modules/billing/components/PaymentUploadDialog.js +115 -0
  181. package/dist/modules/billing/components/PaymentUploadDialog.js.map +7 -0
  182. package/dist/modules/billing/components/PaymentsTable.js +171 -0
  183. package/dist/modules/billing/components/PaymentsTable.js.map +7 -0
  184. package/dist/modules/billing/components/PostRunDraftsButton.js +105 -0
  185. package/dist/modules/billing/components/PostRunDraftsButton.js.map +7 -0
  186. package/dist/modules/billing/components/PrepaidPanel.js +14 -4
  187. package/dist/modules/billing/components/PrepaidPanel.js.map +2 -2
  188. package/dist/modules/billing/components/RunCyclesSection.js +209 -0
  189. package/dist/modules/billing/components/RunCyclesSection.js.map +7 -0
  190. package/dist/modules/billing/components/TestRunActions.js +127 -0
  191. package/dist/modules/billing/components/TestRunActions.js.map +7 -0
  192. package/dist/modules/billing/components/account/AccountItemsTab.js +157 -0
  193. package/dist/modules/billing/components/account/AccountItemsTab.js.map +7 -0
  194. package/dist/modules/billing/components/account/AccountRunBillingDialog.js +122 -0
  195. package/dist/modules/billing/components/account/AccountRunBillingDialog.js.map +7 -0
  196. package/dist/modules/billing/components/account/AccountRunsTab.js +152 -0
  197. package/dist/modules/billing/components/account/AccountRunsTab.js.map +7 -0
  198. package/dist/modules/billing/components/account/AccountStatementsTab.js +98 -0
  199. package/dist/modules/billing/components/account/AccountStatementsTab.js.map +7 -0
  200. package/dist/modules/billing/components/account/AccountSummaryCards.js +125 -0
  201. package/dist/modules/billing/components/account/AccountSummaryCards.js.map +7 -0
  202. package/dist/modules/billing/components/account/AccountUsageTab.js +91 -0
  203. package/dist/modules/billing/components/account/AccountUsageTab.js.map +7 -0
  204. package/dist/modules/billing/components/account/AccountView.js +73 -0
  205. package/dist/modules/billing/components/account/AccountView.js.map +7 -0
  206. package/dist/modules/billing/components/account/accountRow.js +39 -0
  207. package/dist/modules/billing/components/account/accountRow.js.map +7 -0
  208. package/dist/modules/billing/components/customerNames.js +42 -0
  209. package/dist/modules/billing/components/customerNames.js.map +7 -0
  210. package/dist/modules/billing/data/entities.js +615 -59
  211. package/dist/modules/billing/data/entities.js.map +2 -2
  212. package/dist/modules/billing/data/entityIds.js +10 -1
  213. package/dist/modules/billing/data/entityIds.js.map +2 -2
  214. package/dist/modules/billing/data/validators.js +269 -15
  215. package/dist/modules/billing/data/validators.js.map +2 -2
  216. package/dist/modules/billing/events.js +70 -0
  217. package/dist/modules/billing/events.js.map +2 -2
  218. package/dist/modules/billing/i18n/en.json +457 -21
  219. package/dist/modules/billing/i18n/pl.json +457 -21
  220. package/dist/modules/billing/index.js +6 -2
  221. package/dist/modules/billing/index.js.map +2 -2
  222. package/dist/modules/billing/lib/accountFinancials.js +58 -0
  223. package/dist/modules/billing/lib/accountFinancials.js.map +7 -0
  224. package/dist/modules/billing/lib/accountLock.js +14 -0
  225. package/dist/modules/billing/lib/accountLock.js.map +7 -0
  226. package/dist/modules/billing/lib/accountStatus.js +9 -7
  227. package/dist/modules/billing/lib/accountStatus.js.map +2 -2
  228. package/dist/modules/billing/lib/accountSummary.js +106 -0
  229. package/dist/modules/billing/lib/accountSummary.js.map +7 -0
  230. package/dist/modules/billing/lib/allocation.js +356 -0
  231. package/dist/modules/billing/lib/allocation.js.map +7 -0
  232. package/dist/modules/billing/lib/allocationEvents.js +22 -0
  233. package/dist/modules/billing/lib/allocationEvents.js.map +7 -0
  234. package/dist/modules/billing/lib/billPeriod.js +15 -1
  235. package/dist/modules/billing/lib/billPeriod.js.map +2 -2
  236. package/dist/modules/billing/lib/billRunEngine.js +241 -106
  237. package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
  238. package/dist/modules/billing/lib/billRunLabel.js.map +1 -1
  239. package/dist/modules/billing/lib/billingSettings.js +116 -0
  240. package/dist/modules/billing/lib/billingSettings.js.map +7 -0
  241. package/dist/modules/billing/lib/commandRoute.js +81 -0
  242. package/dist/modules/billing/lib/commandRoute.js.map +7 -0
  243. package/dist/modules/billing/lib/customerLookup.js +97 -0
  244. package/dist/modules/billing/lib/customerLookup.js.map +7 -0
  245. package/dist/modules/billing/lib/dateOnly.js +13 -0
  246. package/dist/modules/billing/lib/dateOnly.js.map +7 -0
  247. package/dist/modules/billing/lib/documentParties.js +16 -0
  248. package/dist/modules/billing/lib/documentParties.js.map +7 -0
  249. package/dist/modules/billing/lib/documentTypes.js +27 -0
  250. package/dist/modules/billing/lib/documentTypes.js.map +7 -0
  251. package/dist/modules/billing/lib/dueDate.js +18 -0
  252. package/dist/modules/billing/lib/dueDate.js.map +7 -0
  253. package/dist/modules/billing/lib/iban.js +29 -0
  254. package/dist/modules/billing/lib/iban.js.map +7 -0
  255. package/dist/modules/billing/lib/invoiceDelivery.js +131 -0
  256. package/dist/modules/billing/lib/invoiceDelivery.js.map +7 -0
  257. package/dist/modules/billing/lib/invoiceLanguages.js +16 -0
  258. package/dist/modules/billing/lib/invoiceLanguages.js.map +7 -0
  259. package/dist/modules/billing/lib/invoiceWriter.js +107 -99
  260. package/dist/modules/billing/lib/invoiceWriter.js.map +2 -2
  261. package/dist/modules/billing/lib/itemBillingState.js +27 -0
  262. package/dist/modules/billing/lib/itemBillingState.js.map +7 -0
  263. package/dist/modules/billing/lib/itemPriceChange.js +18 -0
  264. package/dist/modules/billing/lib/itemPriceChange.js.map +7 -0
  265. package/dist/modules/billing/lib/itemSelector.js.map +2 -2
  266. package/dist/modules/billing/lib/lineAmounts.js +32 -0
  267. package/dist/modules/billing/lib/lineAmounts.js.map +7 -0
  268. package/dist/modules/billing/lib/memoGuards.js +25 -0
  269. package/dist/modules/billing/lib/memoGuards.js.map +7 -0
  270. package/dist/modules/billing/lib/navigation.js +48 -0
  271. package/dist/modules/billing/lib/navigation.js.map +7 -0
  272. package/dist/modules/billing/lib/numbering.js +120 -0
  273. package/dist/modules/billing/lib/numbering.js.map +7 -0
  274. package/dist/modules/billing/lib/overdue.js +36 -0
  275. package/dist/modules/billing/lib/overdue.js.map +7 -0
  276. package/dist/modules/billing/lib/paymentCsv.js +125 -0
  277. package/dist/modules/billing/lib/paymentCsv.js.map +7 -0
  278. package/dist/modules/billing/lib/paymentGateway.js +15 -0
  279. package/dist/modules/billing/lib/paymentGateway.js.map +7 -0
  280. package/dist/modules/billing/lib/paymentImport.js +164 -0
  281. package/dist/modules/billing/lib/paymentImport.js.map +7 -0
  282. package/dist/modules/billing/lib/paymentMatcher.js +36 -0
  283. package/dist/modules/billing/lib/paymentMatcher.js.map +7 -0
  284. package/dist/modules/billing/lib/pdf/attachmentStore.js +46 -0
  285. package/dist/modules/billing/lib/pdf/attachmentStore.js.map +7 -0
  286. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js +7 -0
  287. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js.map +7 -0
  288. package/dist/modules/billing/lib/pdf/invoiceModel.js +194 -0
  289. package/dist/modules/billing/lib/pdf/invoiceModel.js.map +7 -0
  290. package/dist/modules/billing/lib/pdf/invoicePdf.js +165 -0
  291. package/dist/modules/billing/lib/pdf/invoicePdf.js.map +7 -0
  292. package/dist/modules/billing/lib/pdf/invoiceTemplate.js +274 -0
  293. package/dist/modules/billing/lib/pdf/invoiceTemplate.js.map +7 -0
  294. package/dist/modules/billing/lib/prepaidConfig.js +17 -25
  295. package/dist/modules/billing/lib/prepaidConfig.js.map +2 -2
  296. package/dist/modules/billing/lib/prepaidConsume.js +26 -7
  297. package/dist/modules/billing/lib/prepaidConsume.js.map +2 -2
  298. package/dist/modules/billing/lib/prepaidPeriodClose.js +277 -158
  299. package/dist/modules/billing/lib/prepaidPeriodClose.js.map +2 -2
  300. package/dist/modules/billing/lib/prepaidTaxPoint.js +37 -0
  301. package/dist/modules/billing/lib/prepaidTaxPoint.js.map +7 -0
  302. package/dist/modules/billing/lib/seeds/demo.js +51 -3
  303. package/dist/modules/billing/lib/seeds/demo.js.map +2 -2
  304. package/dist/modules/billing/lib/seeds/paymentsSample.js +93 -0
  305. package/dist/modules/billing/lib/seeds/paymentsSample.js.map +7 -0
  306. package/dist/modules/billing/lib/seeds/sellerSettings.js +41 -0
  307. package/dist/modules/billing/lib/seeds/sellerSettings.js.map +7 -0
  308. package/dist/modules/billing/lib/servicePeriods.js +121 -0
  309. package/dist/modules/billing/lib/servicePeriods.js.map +7 -0
  310. package/dist/modules/billing/lib/tenantLock.js.map +1 -1
  311. package/dist/modules/billing/lib/testRunCleanup.js +24 -0
  312. package/dist/modules/billing/lib/testRunCleanup.js.map +7 -0
  313. package/dist/modules/billing/lib/topupCapture.js +2 -2
  314. package/dist/modules/billing/lib/topupCapture.js.map +2 -2
  315. package/dist/modules/billing/lib/topupReceipt.js +58 -79
  316. package/dist/modules/billing/lib/topupReceipt.js.map +2 -2
  317. package/dist/modules/billing/lib/usageRater.js.map +1 -1
  318. package/dist/modules/billing/lib/usageRunner.js +14 -6
  319. package/dist/modules/billing/lib/usageRunner.js.map +2 -2
  320. package/dist/modules/billing/lib/vat.js +39 -0
  321. package/dist/modules/billing/lib/vat.js.map +7 -0
  322. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js +15 -0
  323. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js.map +7 -0
  324. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js +25 -0
  325. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js.map +7 -0
  326. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js +115 -0
  327. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js.map +7 -0
  328. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js +47 -0
  329. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js.map +7 -0
  330. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js +89 -0
  331. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js.map +7 -0
  332. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js +17 -0
  333. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js.map +7 -0
  334. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js +27 -0
  335. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js.map +7 -0
  336. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js +29 -0
  337. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js.map +7 -0
  338. package/dist/modules/billing/setup.js +7 -184
  339. package/dist/modules/billing/setup.js.map +2 -2
  340. package/dist/modules/billing/subscribers/credit-memo-auto-send.js +16 -0
  341. package/dist/modules/billing/subscribers/credit-memo-auto-send.js.map +7 -0
  342. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js +16 -0
  343. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js.map +7 -0
  344. package/dist/modules/billing/subscribers/debit-memo-auto-send.js +16 -0
  345. package/dist/modules/billing/subscribers/debit-memo-auto-send.js.map +7 -0
  346. package/dist/modules/billing/subscribers/invoice-apply-credit.js +34 -0
  347. package/dist/modules/billing/subscribers/invoice-apply-credit.js.map +7 -0
  348. package/dist/modules/billing/subscribers/invoice-auto-send.js +30 -0
  349. package/dist/modules/billing/subscribers/invoice-auto-send.js.map +7 -0
  350. package/dist/modules/billing/subscribers/payment-captured-to-paid.js +69 -33
  351. package/dist/modules/billing/subscribers/payment-captured-to-paid.js.map +2 -2
  352. package/dist/modules/billing/subscribers/prepaid-topup-captured.js +4 -6
  353. package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +2 -2
  354. package/dist/modules/billing/workers/reap-stale-bill-runs.js +2 -2
  355. package/dist/modules/billing/workers/reap-stale-bill-runs.js.map +2 -2
  356. package/generated/entities/billing_account/index.ts +26 -0
  357. package/generated/entities/billing_account_balance/index.ts +13 -0
  358. package/generated/entities/billing_account_transaction/index.ts +21 -0
  359. package/generated/entities/billing_account_usage/index.ts +18 -0
  360. package/generated/entities/billing_allocation/index.ts +18 -0
  361. package/generated/entities/billing_document_sequence/index.ts +11 -0
  362. package/generated/entities/billing_draft_invoice_edit/index.ts +14 -0
  363. package/generated/entities/billing_invoice/index.ts +38 -0
  364. package/generated/entities/billing_invoice_line/index.ts +24 -0
  365. package/generated/entities/billing_item/index.ts +26 -0
  366. package/generated/entities/billing_payment/index.ts +25 -0
  367. package/generated/entities/billing_payment_import/index.ts +14 -0
  368. package/generated/entities/billing_run/index.ts +20 -0
  369. package/generated/entities/billing_run_outcome/index.ts +18 -0
  370. package/generated/entities/billing_settings/index.ts +26 -0
  371. package/generated/entities/billing_statement/index.ts +22 -0
  372. package/generated/entities/billing_topup/index.ts +20 -0
  373. package/generated/entities.ids.generated.ts +35 -0
  374. package/package.json +15 -10
  375. package/src/modules/billing/__integration__/TC-BILL-004.spec.ts +170 -0
  376. package/src/modules/billing/__integration__/TC-BILL-005.spec.ts +134 -0
  377. package/src/modules/billing/__integration__/TC-BILL-006.spec.ts +101 -0
  378. package/src/modules/billing/__integration__/TC-BILL-007.spec.ts +140 -0
  379. package/src/modules/billing/__integration__/TC-BILL-008.spec.ts +108 -0
  380. package/src/modules/billing/__integration__/TC-BILL-009.spec.ts +113 -0
  381. package/src/modules/billing/__integration__/TC-BILL-010.spec.ts +118 -0
  382. package/src/modules/billing/__integration__/TC-BILL-011.spec.ts +142 -0
  383. package/src/modules/billing/__integration__/TC-BILL-012.spec.ts +115 -0
  384. package/src/modules/billing/__integration__/TC-BILL-013.spec.ts +118 -0
  385. package/src/modules/billing/__integration__/sellerSettings.ts +24 -0
  386. package/src/modules/billing/__tests__/commands/invoices.test.ts +437 -355
  387. package/src/modules/billing/__tests__/commands/itemsBulk.test.ts +33 -0
  388. package/src/modules/billing/__tests__/commands/memos.test.ts +205 -0
  389. package/src/modules/billing/__tests__/commands/payments.test.ts +241 -0
  390. package/src/modules/billing/__tests__/commands/runsDelete.test.ts +146 -0
  391. package/src/modules/billing/__tests__/commands/runsPostDrafts.test.ts +106 -0
  392. package/src/modules/billing/__tests__/commands/topups.test.ts +11 -0
  393. package/src/modules/billing/__tests__/entityRegistry.test.ts +326 -0
  394. package/src/modules/billing/__tests__/events.test.ts +142 -0
  395. package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +28 -11
  396. package/src/modules/billing/__tests__/lib/accountFinancials.test.ts +53 -0
  397. package/src/modules/billing/__tests__/lib/accountSummary.test.ts +97 -0
  398. package/src/modules/billing/__tests__/lib/allocation.test.ts +319 -0
  399. package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +231 -20
  400. package/src/modules/billing/__tests__/lib/billingSettings.test.ts +109 -0
  401. package/src/modules/billing/__tests__/lib/customerLookup.test.ts +156 -0
  402. package/src/modules/billing/__tests__/lib/dueDate.test.ts +35 -0
  403. package/src/modules/billing/__tests__/lib/iban.test.ts +38 -0
  404. package/src/modules/billing/__tests__/lib/invoiceDelivery.test.ts +189 -0
  405. package/src/modules/billing/__tests__/lib/invoiceWriter.test.ts +212 -10
  406. package/src/modules/billing/__tests__/lib/itemBillingState.test.ts +42 -0
  407. package/src/modules/billing/__tests__/lib/itemPriceChange.test.ts +165 -0
  408. package/src/modules/billing/__tests__/lib/lineAmounts.test.ts +86 -0
  409. package/src/modules/billing/__tests__/lib/navigation.test.ts +71 -0
  410. package/src/modules/billing/__tests__/lib/numbering.test.ts +91 -0
  411. package/src/modules/billing/__tests__/lib/overdue.test.ts +36 -0
  412. package/src/modules/billing/__tests__/lib/paymentCsv.test.ts +148 -0
  413. package/src/modules/billing/__tests__/lib/paymentImport.test.ts +113 -0
  414. package/src/modules/billing/__tests__/lib/paymentsSample.test.ts +73 -0
  415. package/src/modules/billing/__tests__/lib/pdf/invoicePdf.test.ts +199 -0
  416. package/src/modules/billing/__tests__/lib/pdf/invoicePdfService.test.ts +147 -0
  417. package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +374 -98
  418. package/src/modules/billing/__tests__/lib/prepaidTaxPoint.test.ts +118 -0
  419. package/src/modules/billing/__tests__/lib/seedsDemo.test.ts +53 -2
  420. package/src/modules/billing/__tests__/lib/sellerSettings.test.ts +28 -0
  421. package/src/modules/billing/__tests__/lib/servicePeriods.test.ts +190 -0
  422. package/src/modules/billing/__tests__/lib/topupCapture.test.ts +6 -28
  423. package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +21 -52
  424. package/src/modules/billing/__tests__/lib/vat.test.ts +51 -0
  425. package/src/modules/billing/__tests__/package-isolation.test.ts +22 -8
  426. package/src/modules/billing/__tests__/setup.test.ts +13 -4
  427. package/src/modules/billing/__tests__/validators.test.ts +193 -3
  428. package/src/modules/billing/acl.ts +12 -0
  429. package/src/modules/billing/api/accounts/[id]/summary/route.ts +62 -0
  430. package/src/modules/billing/api/accounts/route.ts +58 -4
  431. package/src/modules/billing/api/export/account/[id]/route.ts +20 -18
  432. package/src/modules/billing/api/invoices/[id]/memos/route.ts +63 -0
  433. package/src/modules/billing/api/invoices/[id]/pdf/route.ts +79 -0
  434. package/src/modules/billing/api/invoices/[id]/route.ts +57 -70
  435. package/src/modules/billing/api/invoices/[id]/send/route.ts +127 -0
  436. package/src/modules/billing/api/invoices/add-line/route.ts +3 -3
  437. package/src/modules/billing/api/invoices/delete-draft/route.ts +33 -0
  438. package/src/modules/billing/api/invoices/edit-line/route.ts +4 -4
  439. package/src/modules/billing/api/invoices/post/route.ts +7 -6
  440. package/src/modules/billing/api/invoices/remove-line/route.ts +3 -3
  441. package/src/modules/billing/api/invoices/route.ts +81 -86
  442. package/src/modules/billing/api/items/bulk/route.ts +2 -1
  443. package/src/modules/billing/api/items/change-price/route.ts +54 -0
  444. package/src/modules/billing/api/items/route.ts +23 -7
  445. package/src/modules/billing/api/payment-imports/[id]/route.ts +49 -0
  446. package/src/modules/billing/api/payment-imports/route.ts +57 -0
  447. package/src/modules/billing/api/payments/[id]/route.ts +63 -0
  448. package/src/modules/billing/api/payments/allocate/route.ts +35 -0
  449. package/src/modules/billing/api/payments/assign/route.ts +36 -0
  450. package/src/modules/billing/api/payments/convert-to-topup/route.ts +35 -0
  451. package/src/modules/billing/api/payments/ignore/route.ts +35 -0
  452. package/src/modules/billing/api/payments/import/route.ts +72 -0
  453. package/src/modules/billing/api/payments/restore/route.ts +35 -0
  454. package/src/modules/billing/api/payments/route.ts +147 -0
  455. package/src/modules/billing/api/payments/unallocate/route.ts +35 -0
  456. package/src/modules/billing/api/payments/unassign/route.ts +35 -0
  457. package/src/modules/billing/api/run-outcomes/route.ts +50 -7
  458. package/src/modules/billing/api/runs/[id]/post-drafts/route.ts +71 -0
  459. package/src/modules/billing/api/runs/[id]/route.ts +59 -0
  460. package/src/modules/billing/api/runs/retry-failed/route.ts +4 -3
  461. package/src/modules/billing/api/runs/route.ts +17 -9
  462. package/src/modules/billing/api/settings/number-preview/route.ts +46 -0
  463. package/src/modules/billing/api/settings/route.ts +116 -0
  464. package/src/modules/billing/api/statements/route.ts +5 -0
  465. package/src/modules/billing/api/test-invoices/wipe/route.ts +2 -2
  466. package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +151 -372
  467. package/src/modules/billing/backend/billing/accounts/create/page.tsx +6 -0
  468. package/src/modules/billing/backend/billing/accounts/page.tsx +71 -5
  469. package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +445 -96
  470. package/src/modules/billing/backend/billing/invoices/page.tsx +8 -227
  471. package/src/modules/billing/backend/billing/items/[id]/page.tsx +75 -6
  472. package/src/modules/billing/backend/billing/items/create/page.tsx +13 -8
  473. package/src/modules/billing/backend/billing/items/page.tsx +19 -3
  474. package/src/modules/billing/backend/billing/payments/[id]/page.meta.ts +19 -0
  475. package/src/modules/billing/backend/billing/payments/[id]/page.tsx +415 -0
  476. package/src/modules/billing/backend/billing/payments/imports/[id]/page.meta.ts +19 -0
  477. package/src/modules/billing/backend/billing/payments/imports/[id]/page.tsx +106 -0
  478. package/src/modules/billing/backend/billing/payments/page.meta.ts +16 -0
  479. package/src/modules/billing/backend/billing/payments/page.tsx +56 -0
  480. package/src/modules/billing/backend/billing/runs/[id]/page.tsx +50 -19
  481. package/src/modules/billing/backend/billing/runs/page.tsx +60 -9
  482. package/src/modules/billing/backend/billing/statements/[id]/page.tsx +87 -6
  483. package/src/modules/billing/backend/billing/statements/page.tsx +2 -2
  484. package/src/modules/billing/backend/billing/topups/page.tsx +2 -2
  485. package/src/modules/billing/backend/billing/transactions/page.tsx +2 -2
  486. package/src/modules/billing/backend/billing/usage/create/page.tsx +3 -2
  487. package/src/modules/billing/backend/config/billing/page.meta.ts +21 -0
  488. package/src/modules/billing/backend/config/billing/page.tsx +549 -0
  489. package/src/modules/billing/ce.ts +8 -7
  490. package/src/modules/billing/cli.ts +31 -1
  491. package/src/modules/billing/commands/accounts.ts +6 -0
  492. package/src/modules/billing/commands/index.ts +3 -0
  493. package/src/modules/billing/commands/invoices.ts +371 -238
  494. package/src/modules/billing/commands/items.ts +143 -0
  495. package/src/modules/billing/commands/memos.ts +218 -0
  496. package/src/modules/billing/commands/payments.ts +428 -0
  497. package/src/modules/billing/commands/runs.ts +208 -6
  498. package/src/modules/billing/commands/settings.ts +77 -0
  499. package/src/modules/billing/commands/topups.ts +12 -3
  500. package/src/modules/billing/commands/transactions.ts +2 -2
  501. package/src/modules/billing/components/AccountForm.tsx +199 -53
  502. package/src/modules/billing/components/AccountInvoicesSection.tsx +75 -0
  503. package/src/modules/billing/components/AccountPaymentsSection.tsx +42 -0
  504. package/src/modules/billing/components/AccountPicker.tsx +70 -0
  505. package/src/modules/billing/components/AllocatePaymentDialog.tsx +167 -0
  506. package/src/modules/billing/components/BillingDataTable.tsx +34 -0
  507. package/src/modules/billing/components/BillingSettingsLink.tsx +25 -0
  508. package/src/modules/billing/components/ChangeItemPriceDialog.tsx +180 -0
  509. package/src/modules/billing/components/CreateMemoDialog.tsx +232 -0
  510. package/src/modules/billing/components/CustomerLabel.tsx +53 -0
  511. package/src/modules/billing/components/InvoicesTable.tsx +344 -0
  512. package/src/modules/billing/components/ItemForm.tsx +233 -44
  513. package/src/modules/billing/components/ItemPriceField.tsx +81 -0
  514. package/src/modules/billing/components/LineFormDialog.tsx +25 -1
  515. package/src/modules/billing/components/ManualPaymentDialog.tsx +140 -0
  516. package/src/modules/billing/components/MoneyInput.tsx +26 -0
  517. package/src/modules/billing/components/PaymentUploadDialog.tsx +148 -0
  518. package/src/modules/billing/components/PaymentsTable.tsx +213 -0
  519. package/src/modules/billing/components/PostRunDraftsButton.tsx +129 -0
  520. package/src/modules/billing/components/PrepaidPanel.tsx +20 -7
  521. package/src/modules/billing/components/RunCyclesSection.tsx +298 -0
  522. package/src/modules/billing/components/TestRunActions.tsx +152 -0
  523. package/src/modules/billing/components/account/AccountItemsTab.tsx +192 -0
  524. package/src/modules/billing/components/account/AccountRunBillingDialog.tsx +147 -0
  525. package/src/modules/billing/components/account/AccountRunsTab.tsx +201 -0
  526. package/src/modules/billing/components/account/AccountStatementsTab.tsx +120 -0
  527. package/src/modules/billing/components/account/AccountSummaryCards.tsx +153 -0
  528. package/src/modules/billing/components/account/AccountUsageTab.tsx +122 -0
  529. package/src/modules/billing/components/account/AccountView.tsx +101 -0
  530. package/src/modules/billing/components/account/accountRow.ts +70 -0
  531. package/src/modules/billing/components/customerNames.ts +62 -0
  532. package/src/modules/billing/data/entities.ts +685 -15
  533. package/src/modules/billing/data/entityIds.ts +25 -11
  534. package/src/modules/billing/data/validators.ts +388 -8
  535. package/src/modules/billing/events.ts +82 -3
  536. package/src/modules/billing/i18n/en.json +457 -21
  537. package/src/modules/billing/i18n/pl.json +457 -21
  538. package/src/modules/billing/index.ts +6 -2
  539. package/src/modules/billing/lib/accountFinancials.ts +84 -0
  540. package/src/modules/billing/lib/accountLock.ts +32 -0
  541. package/src/modules/billing/lib/accountStatus.ts +11 -9
  542. package/src/modules/billing/lib/accountSummary.ts +172 -0
  543. package/src/modules/billing/lib/allocation.ts +515 -0
  544. package/src/modules/billing/lib/allocationEvents.ts +26 -0
  545. package/src/modules/billing/lib/billPeriod.ts +19 -0
  546. package/src/modules/billing/lib/billRunEngine.ts +362 -162
  547. package/src/modules/billing/lib/billRunLabel.ts +1 -1
  548. package/src/modules/billing/lib/billingSettings.ts +145 -0
  549. package/src/modules/billing/lib/commandRoute.ts +112 -0
  550. package/src/modules/billing/lib/customerLookup.ts +157 -0
  551. package/src/modules/billing/lib/dateOnly.ts +22 -0
  552. package/src/modules/billing/lib/documentParties.ts +23 -0
  553. package/src/modules/billing/lib/documentTypes.ts +37 -0
  554. package/src/modules/billing/lib/dueDate.ts +26 -0
  555. package/src/modules/billing/lib/iban.ts +36 -0
  556. package/src/modules/billing/lib/invoiceDelivery.ts +197 -0
  557. package/src/modules/billing/lib/invoiceLanguages.ts +34 -0
  558. package/src/modules/billing/lib/invoiceWriter.ts +162 -148
  559. package/src/modules/billing/lib/itemBillingState.ts +67 -0
  560. package/src/modules/billing/lib/itemPriceChange.ts +56 -0
  561. package/src/modules/billing/lib/itemSelector.ts +4 -1
  562. package/src/modules/billing/lib/lineAmounts.ts +61 -0
  563. package/src/modules/billing/lib/memoGuards.ts +41 -0
  564. package/src/modules/billing/lib/navigation.ts +64 -0
  565. package/src/modules/billing/lib/numbering.ts +174 -0
  566. package/src/modules/billing/lib/overdue.ts +60 -0
  567. package/src/modules/billing/lib/paymentCsv.ts +171 -0
  568. package/src/modules/billing/lib/paymentGateway.ts +52 -0
  569. package/src/modules/billing/lib/paymentImport.ts +228 -0
  570. package/src/modules/billing/lib/paymentMatcher.ts +88 -0
  571. package/src/modules/billing/lib/pdf/attachmentStore.ts +60 -0
  572. package/src/modules/billing/lib/pdf/fonts/NotoSans-Bold.ttf +0 -0
  573. package/src/modules/billing/lib/pdf/fonts/NotoSans-Regular.ttf +0 -0
  574. package/src/modules/billing/lib/pdf/fonts/OFL.txt +93 -0
  575. package/src/modules/billing/lib/pdf/fonts/embeddedFonts.ts +9 -0
  576. package/src/modules/billing/lib/pdf/invoiceModel.ts +328 -0
  577. package/src/modules/billing/lib/pdf/invoicePdf.ts +216 -0
  578. package/src/modules/billing/lib/pdf/invoiceTemplate.ts +338 -0
  579. package/src/modules/billing/lib/prepaidConfig.ts +27 -36
  580. package/src/modules/billing/lib/prepaidConsume.ts +29 -6
  581. package/src/modules/billing/lib/prepaidPeriodClose.ts +410 -242
  582. package/src/modules/billing/lib/prepaidTaxPoint.ts +76 -0
  583. package/src/modules/billing/lib/seeds/demo.ts +79 -6
  584. package/src/modules/billing/lib/seeds/paymentsSample.ts +136 -0
  585. package/src/modules/billing/lib/seeds/sellerSettings.ts +78 -0
  586. package/src/modules/billing/lib/servicePeriods.ts +226 -0
  587. package/src/modules/billing/lib/tenantLock.ts +1 -1
  588. package/src/modules/billing/lib/testRunCleanup.ts +42 -0
  589. package/src/modules/billing/lib/topupCapture.ts +3 -4
  590. package/src/modules/billing/lib/topupReceipt.ts +70 -103
  591. package/src/modules/billing/lib/usageRater.ts +1 -1
  592. package/src/modules/billing/lib/usageRunner.ts +17 -5
  593. package/src/modules/billing/lib/vat.ts +56 -0
  594. package/src/modules/billing/migrations/Migration20261114000000_billing_customer_kind.ts +25 -0
  595. package/src/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.ts +37 -0
  596. package/src/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.ts +132 -0
  597. package/src/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.ts +58 -0
  598. package/src/modules/billing/migrations/Migration20261117000000_billing_payments.ts +97 -0
  599. package/src/modules/billing/migrations/Migration20261118000000_billing_memos.ts +21 -0
  600. package/src/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.ts +39 -0
  601. package/src/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.ts +35 -0
  602. package/src/modules/billing/setup.ts +11 -237
  603. package/src/modules/billing/subscribers/credit-memo-auto-send.ts +15 -0
  604. package/src/modules/billing/subscribers/debit-memo-apply-credit.ts +15 -0
  605. package/src/modules/billing/subscribers/debit-memo-auto-send.ts +15 -0
  606. package/src/modules/billing/subscribers/invoice-apply-credit.ts +39 -0
  607. package/src/modules/billing/subscribers/invoice-auto-send.ts +46 -0
  608. package/src/modules/billing/subscribers/payment-captured-to-paid.ts +84 -62
  609. package/src/modules/billing/subscribers/prepaid-topup-captured.ts +4 -8
  610. package/src/modules/billing/workers/reap-stale-bill-runs.ts +4 -4
  611. package/dist/modules/billing/lib/invoiceStatus.js +0 -38
  612. package/dist/modules/billing/lib/invoiceStatus.js.map +0 -7
  613. package/src/modules/billing/__tests__/lib/invoiceStatus.test.ts +0 -97
  614. package/src/modules/billing/lib/invoiceStatus.ts +0 -61
@@ -2,7 +2,6 @@ import { z } from "zod";
2
2
  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
- import { SalesInvoice } from "@open-mercato/core/modules/sales/data/entities";
6
5
  import { billingEntityIds } from "../../../data/entityIds.js";
7
6
  import {
8
7
  billingInvoiceAddLineSchema,
@@ -10,6 +9,7 @@ import {
10
9
  billingInvoiceRemoveLineSchema
11
10
  } from "../../../data/validators.js";
12
11
  import { createBillingCrudOpenApi } from "../../openapi.js";
12
+ import { BillingInvoice } from "../../../data/entities.js";
13
13
  const routeMetadata = {
14
14
  POST: { requireAuth: true, requireFeatures: ["billing.invoice.edit_draft"] }
15
15
  };
@@ -23,7 +23,7 @@ const rawBodySchema = z.object({}).passthrough();
23
23
  const crud = makeCrudRoute({
24
24
  metadata: routeMetadata,
25
25
  orm: {
26
- entity: SalesInvoice,
26
+ entity: BillingInvoice,
27
27
  idField: "id",
28
28
  orgField: "organizationId",
29
29
  tenantField: "tenantId",
@@ -62,7 +62,7 @@ const openApi = createBillingCrudOpenApi({
62
62
  create: {
63
63
  schema: billingInvoiceEditLineSchema,
64
64
  responseSchema: editLineResponseSchema,
65
- description: "Edits one line of a draft invoice (description, quantity, unit_price, or explicit `totalNetAmount` override). Writes a `DraftInvoiceEdit` audit row with before+after snapshots and recomputes invoice totals. Refuses non-draft invoices with HTTP 409 + `billing.invoice.not_draft`."
65
+ description: "Edits one line of a draft invoice (description, quantity, unit_price, or explicit `totalNetAmount` override). Writes a `BillingDraftInvoiceEdit` audit row with before+after snapshots and recomputes invoice totals. Refuses non-draft invoices with HTTP 409 + `billing.invoice.not_draft`."
66
66
  }
67
67
  });
68
68
  const _phaseSchemaRefs = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/billing/api/invoices/edit-line/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 { SalesInvoice } from '@open-mercato/core/modules/sales/data/entities'\nimport { billingEntityIds } from '../../../data/entityIds'\nimport {\n billingInvoiceAddLineSchema,\n billingInvoiceEditLineSchema,\n billingInvoiceRemoveLineSchema,\n} from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\n\n/**\n * `POST /api/billing/invoices/edit-line` \u2014 edits one line of a draft\n * invoice. Refuses non-drafts (409 / `billing.invoice.not_draft`).\n * Writes a `DraftInvoiceEdit` audit row per spec. Recomputes invoice\n * totals from the surviving lines after the edit.\n *\n * Operator surface: route is gated by `billing.invoice.edit_draft`.\n * Future Phase 4c admin UI calls into the same command via this route.\n */\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.invoice.edit_draft'] },\n}\n\nexport const metadata = routeMetadata\n\nconst editLineResponseSchema = z.object({\n invoiceId: z.string().uuid(),\n invoiceLineId: z.string().uuid(),\n auditId: z.string().uuid(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesInvoice,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n entityId: billingEntityIds.draftInvoiceEdit,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.invoices.edit_draft_line',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingInvoiceEditLineSchema.parse(scoped)\n },\n response: ({ result }) => ({\n invoiceId: result?.invoiceId ?? null,\n invoiceLineId: result?.invoiceLineId ?? null,\n auditId: result?.auditId ?? null,\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingInvoiceLineEdit',\n pluralName: 'BillingInvoiceLineEdits',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingInvoiceEditLineSchema,\n responseSchema: editLineResponseSchema,\n description:\n 'Edits one line of a draft invoice (description, quantity, ' +\n \"unit_price, or explicit `totalNetAmount` override). Writes a \" +\n '`DraftInvoiceEdit` audit row with before+after snapshots and ' +\n 'recomputes invoice totals. Refuses non-draft invoices with ' +\n 'HTTP 409 + `billing.invoice.not_draft`.',\n },\n})\n\n// Re-export the add/remove route schemas so the OpenAPI bundle keeps\n// them discoverable from a single import surface \u2014 the dedicated\n// routes (sibling folders) own the actual handlers.\nexport const _phaseSchemaRefs = {\n add: billingInvoiceAddLineSchema,\n remove: billingInvoiceRemoveLineSchema,\n}\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gCAAgC;AAYzC,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC7E;AAEO,MAAM,WAAW;AAExB,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,SAAS,EAAE,OAAO,EAAE,KAAK;AAC3B,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,IACjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,6BAA6B,MAAM,MAAM;AAAA,MAClD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,WAAW,QAAQ,aAAa;AAAA,QAChC,eAAe,QAAQ,iBAAiB;AAAA,QACxC,SAAS,QAAQ,WAAW;AAAA,MAC9B;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAKJ;AACF,CAAC;AAKM,MAAM,mBAAmB;AAAA,EAC9B,KAAK;AAAA,EACL,QAAQ;AACV;",
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 { billingEntityIds } from '../../../data/entityIds'\nimport {\n billingInvoiceAddLineSchema,\n billingInvoiceEditLineSchema,\n billingInvoiceRemoveLineSchema,\n} from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\nimport { BillingInvoice } from '../../../data/entities'\n\n/**\n * `POST /api/billing/invoices/edit-line` \u2014 edits one line of a draft\n * invoice. Refuses non-drafts (409 / `billing.invoice.not_draft`).\n * Writes a `BillingDraftInvoiceEdit` audit row per spec. Recomputes invoice\n * totals from the surviving lines after the edit.\n *\n * Operator surface: route is gated by `billing.invoice.edit_draft`.\n * Future Phase 4c admin UI calls into the same command via this route.\n */\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.invoice.edit_draft'] },\n}\n\nexport const metadata = routeMetadata\n\nconst editLineResponseSchema = z.object({\n invoiceId: z.string().uuid(),\n invoiceLineId: z.string().uuid(),\n auditId: z.string().uuid(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: BillingInvoice,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n entityId: billingEntityIds.draftInvoiceEdit,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.invoices.edit_draft_line',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingInvoiceEditLineSchema.parse(scoped)\n },\n response: ({ result }) => ({\n invoiceId: result?.invoiceId ?? null,\n invoiceLineId: result?.invoiceLineId ?? null,\n auditId: result?.auditId ?? null,\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingInvoiceLineEdit',\n pluralName: 'BillingInvoiceLineEdits',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingInvoiceEditLineSchema,\n responseSchema: editLineResponseSchema,\n description:\n 'Edits one line of a draft invoice (description, quantity, ' +\n \"unit_price, or explicit `totalNetAmount` override). Writes a \" +\n '`BillingDraftInvoiceEdit` audit row with before+after snapshots and ' +\n 'recomputes invoice totals. Refuses non-draft invoices with ' +\n 'HTTP 409 + `billing.invoice.not_draft`.',\n },\n})\n\n// Re-export the add/remove route schemas so the OpenAPI bundle keeps\n// them discoverable from a single import surface \u2014 the dedicated\n// routes (sibling folders) own the actual handlers.\nexport const _phaseSchemaRefs = {\n add: billingInvoiceAddLineSchema,\n remove: billingInvoiceRemoveLineSchema,\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAY/B,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC7E;AAEO,MAAM,WAAW;AAExB,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,SAAS,EAAE,OAAO,EAAE,KAAK;AAC3B,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,IACjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,6BAA6B,MAAM,MAAM;AAAA,MAClD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,WAAW,QAAQ,aAAa;AAAA,QAChC,eAAe,QAAQ,iBAAiB;AAAA,QACxC,SAAS,QAAQ,WAAW;AAAA,MAC9B;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAKJ;AACF,CAAC;AAKM,MAAM,mBAAmB;AAAA,EAC9B,KAAK;AAAA,EACL,QAAQ;AACV;",
6
6
  "names": []
7
7
  }
@@ -2,10 +2,10 @@ import { z } from "zod";
2
2
  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
- import { SalesInvoice } from "@open-mercato/core/modules/sales/data/entities";
6
5
  import { billingEntityIds } from "../../../data/entityIds.js";
7
6
  import { billingInvoicePostSchema } from "../../../data/validators.js";
8
7
  import { createBillingCrudOpenApi } from "../../openapi.js";
8
+ import { BillingInvoice } from "../../../data/entities.js";
9
9
  const routeMetadata = {
10
10
  POST: { requireAuth: true, requireFeatures: ["billing.invoice.post"] }
11
11
  };
@@ -20,7 +20,7 @@ const rawBodySchema = z.object({}).passthrough();
20
20
  const crud = makeCrudRoute({
21
21
  metadata: routeMetadata,
22
22
  orm: {
23
- entity: SalesInvoice,
23
+ entity: BillingInvoice,
24
24
  idField: "id",
25
25
  orgField: "organizationId",
26
26
  tenantField: "tenantId",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/billing/api/invoices/post/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 { SalesInvoice } from '@open-mercato/core/modules/sales/data/entities'\nimport { billingEntityIds } from '../../../data/entityIds'\nimport { billingInvoicePostSchema } from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\n\n/**\n * `POST /api/billing/invoices/post`\n *\n * Operator action: flip a billing-drafted invoice from `draft` \u2192\n * `posted`. The command refuses non-draft inputs (409 with\n * `billing.invoice.not_draft`) and test-mode invoices (409 with\n * `billing.invoice.test_run`). The invoice number is NOT re-assigned\n * (it was set at draft create \u2014 see Phase 2 deviation note in the\n * spec).\n *\n * Implemented as a create-only `makeCrudRoute` so it inherits the same\n * RBAC + OpenAPI plumbing as the rest of the billing surface. The\n * `entity` is `SalesInvoice` only because the factory requires one;\n * the GET half is never exposed on this URL.\n */\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.invoice.post'] },\n}\n\nexport const metadata = routeMetadata\n\nconst postResponseSchema = z.object({\n invoiceId: z.string().uuid(),\n invoiceNumber: z.string(),\n status: z.literal('posted'),\n lineCount: z.number().int().nonnegative(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesInvoice,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n // `entityId` is technically a core/sales concept, but the factory\n // only needs it as an identifier \u2014 using a billing-namespaced one\n // keeps the registry tidy (no surprise list endpoints).\n entityId: billingEntityIds.draftInvoiceEdit,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.invoices.post',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingInvoicePostSchema.parse(scoped)\n },\n response: ({ result }) => ({\n invoiceId: result?.invoiceId ?? null,\n invoiceNumber: result?.invoiceNumber ?? null,\n status: result?.status ?? null,\n lineCount: result?.lineCount ?? 0,\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingInvoicePost',\n pluralName: 'BillingInvoicePosts',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingInvoicePostSchema,\n responseSchema: postResponseSchema,\n description:\n 'Flips a billing-drafted `core/sales` invoice from `draft` to ' +\n '`posted`. Refuses non-draft invoices (409 / ' +\n '`billing.invoice.not_draft`) and test-mode invoices (409 / ' +\n '`billing.invoice.test_run`). Fires `billing.invoice.posted` + ' +\n 'one `billing.invoice.line_posted` per line. The invoice number ' +\n 'is NOT re-assigned (set at draft create \u2014 see Phase 2 deviation).',\n },\n})\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AAkBzC,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,sBAAsB,EAAE;AACvE;AAEO,MAAM,WAAW;AAExB,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,eAAe,EAAE,OAAO;AAAA,EACxB,QAAQ,EAAE,QAAQ,QAAQ;AAAA,EAC1B,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAC1C,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA;AAAA;AAAA;AAAA,IAIjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,yBAAyB,MAAM,MAAM;AAAA,MAC9C;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,WAAW,QAAQ,aAAa;AAAA,QAChC,eAAe,QAAQ,iBAAiB;AAAA,QACxC,QAAQ,QAAQ,UAAU;AAAA,QAC1B,WAAW,QAAQ,aAAa;AAAA,MAClC;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAMJ;AACF,CAAC;",
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 { billingEntityIds } from '../../../data/entityIds'\nimport { billingInvoicePostSchema } from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\nimport { BillingInvoice } from '../../../data/entities'\n\n/**\n * `POST /api/billing/invoices/post`\n *\n * Operator action: flip a billing-drafted invoice from `draft` \u2192\n * `posted`. The command refuses non-draft inputs (409 with\n * `billing.invoice.not_draft`) and test-mode invoices (409 with\n * `billing.invoice.test_run`). The series number is claimed at post\n * (Spec 0 numbering masks) \u2014 drafts carry a temporary `DRAFT-\u2026` number.\n * 409 `billing.invoice.number_conflict` when the mask repeats an issued\n * number.\n *\n * Implemented as a create-only `makeCrudRoute` so it inherits the same\n * RBAC + OpenAPI plumbing as the rest of the billing surface. The\n * `entity` is `BillingInvoice` only because the factory requires one;\n * the GET half is never exposed on this URL.\n */\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.invoice.post'] },\n}\n\nexport const metadata = routeMetadata\n\nconst postResponseSchema = z.object({\n invoiceId: z.string().uuid(),\n invoiceNumber: z.string(),\n status: z.literal('posted'),\n lineCount: z.number().int().nonnegative(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: BillingInvoice,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n // `entityId` is technically a core/sales concept, but the factory\n // only needs it as an identifier \u2014 using a billing-namespaced one\n // keeps the registry tidy (no surprise list endpoints).\n entityId: billingEntityIds.draftInvoiceEdit,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.invoices.post',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingInvoicePostSchema.parse(scoped)\n },\n response: ({ result }) => ({\n invoiceId: result?.invoiceId ?? null,\n invoiceNumber: result?.invoiceNumber ?? null,\n status: result?.status ?? null,\n lineCount: result?.lineCount ?? 0,\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingInvoicePost',\n pluralName: 'BillingInvoicePosts',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingInvoicePostSchema,\n responseSchema: postResponseSchema,\n description:\n 'Flips a billing-drafted `core/sales` invoice from `draft` to ' +\n '`posted`. Refuses non-draft invoices (409 / ' +\n '`billing.invoice.not_draft`) and test-mode invoices (409 / ' +\n '`billing.invoice.test_run`). Fires `billing.invoice.posted` + ' +\n 'one `billing.invoice.line_posted` per line. The invoice number ' +\n 'is NOT re-assigned (set at draft create \u2014 see Phase 2 deviation).',\n },\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AACjC,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAmB/B,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,sBAAsB,EAAE;AACvE;AAEO,MAAM,WAAW;AAExB,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,eAAe,EAAE,OAAO;AAAA,EACxB,QAAQ,EAAE,QAAQ,QAAQ;AAAA,EAC1B,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAC1C,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA;AAAA;AAAA;AAAA,IAIjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,yBAAyB,MAAM,MAAM;AAAA,MAC9C;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,WAAW,QAAQ,aAAa;AAAA,QAChC,eAAe,QAAQ,iBAAiB;AAAA,QACxC,QAAQ,QAAQ,UAAU;AAAA,QAC1B,WAAW,QAAQ,aAAa;AAAA,MAClC;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAMJ;AACF,CAAC;",
6
6
  "names": []
7
7
  }
@@ -2,10 +2,10 @@ import { z } from "zod";
2
2
  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
- import { SalesInvoice } from "@open-mercato/core/modules/sales/data/entities";
6
5
  import { billingEntityIds } from "../../../data/entityIds.js";
7
6
  import { billingInvoiceRemoveLineSchema } from "../../../data/validators.js";
8
7
  import { createBillingCrudOpenApi } from "../../openapi.js";
8
+ import { BillingInvoice } from "../../../data/entities.js";
9
9
  const routeMetadata = {
10
10
  POST: { requireAuth: true, requireFeatures: ["billing.invoice.edit_draft"] }
11
11
  };
@@ -19,7 +19,7 @@ const rawBodySchema = z.object({}).passthrough();
19
19
  const crud = makeCrudRoute({
20
20
  metadata: routeMetadata,
21
21
  orm: {
22
- entity: SalesInvoice,
22
+ entity: BillingInvoice,
23
23
  idField: "id",
24
24
  orgField: "organizationId",
25
25
  tenantField: "tenantId",
@@ -58,7 +58,7 @@ const openApi = createBillingCrudOpenApi({
58
58
  create: {
59
59
  schema: billingInvoiceRemoveLineSchema,
60
60
  responseSchema: removeLineResponseSchema,
61
- description: "Removes one line from a draft invoice (hard delete \u2014 the line never existed as far as future readers are concerned). Writes a `DraftInvoiceEdit` audit row (action=`line_removed`, `after_json=null`) carrying the before-snapshot for forensics. Recomputes invoice totals. Refuses non-draft invoices."
61
+ description: "Removes one line from a draft invoice (hard delete \u2014 the line never existed as far as future readers are concerned). Writes a `BillingDraftInvoiceEdit` audit row (action=`line_removed`, `after_json=null`) carrying the before-snapshot for forensics. Recomputes invoice totals. Refuses non-draft invoices."
62
62
  }
63
63
  });
64
64
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/billing/api/invoices/remove-line/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 { SalesInvoice } from '@open-mercato/core/modules/sales/data/entities'\nimport { billingEntityIds } from '../../../data/entityIds'\nimport { billingInvoiceRemoveLineSchema } from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.invoice.edit_draft'] },\n}\n\nexport const metadata = routeMetadata\n\nconst removeLineResponseSchema = z.object({\n invoiceId: z.string().uuid(),\n invoiceLineId: z.string().uuid(),\n auditId: z.string().uuid(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesInvoice,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n entityId: billingEntityIds.draftInvoiceEdit,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.invoices.remove_draft_line',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingInvoiceRemoveLineSchema.parse(scoped)\n },\n response: ({ result }) => ({\n invoiceId: result?.invoiceId ?? null,\n invoiceLineId: result?.invoiceLineId ?? null,\n auditId: result?.auditId ?? null,\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingInvoiceLineRemove',\n pluralName: 'BillingInvoiceLineRemoves',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingInvoiceRemoveLineSchema,\n responseSchema: removeLineResponseSchema,\n description:\n 'Removes one line from a draft invoice (hard delete \u2014 the line ' +\n 'never existed as far as future readers are concerned). Writes ' +\n 'a `DraftInvoiceEdit` audit row (action=`line_removed`, ' +\n '`after_json=null`) carrying the before-snapshot for forensics. ' +\n 'Recomputes invoice totals. Refuses non-draft invoices.',\n },\n})\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC,SAAS,sCAAsC;AAC/C,SAAS,gCAAgC;AAEzC,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC7E;AAEO,MAAM,WAAW;AAExB,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,SAAS,EAAE,OAAO,EAAE,KAAK;AAC3B,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,IACjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,+BAA+B,MAAM,MAAM;AAAA,MACpD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,WAAW,QAAQ,aAAa;AAAA,QAChC,eAAe,QAAQ,iBAAiB;AAAA,QACxC,SAAS,QAAQ,WAAW;AAAA,MAC9B;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAKJ;AACF,CAAC;",
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 { billingEntityIds } from '../../../data/entityIds'\nimport { billingInvoiceRemoveLineSchema } from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\nimport { BillingInvoice } from '../../../data/entities'\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.invoice.edit_draft'] },\n}\n\nexport const metadata = routeMetadata\n\nconst removeLineResponseSchema = z.object({\n invoiceId: z.string().uuid(),\n invoiceLineId: z.string().uuid(),\n auditId: z.string().uuid(),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: BillingInvoice,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n entityId: billingEntityIds.draftInvoiceEdit,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.invoices.remove_draft_line',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingInvoiceRemoveLineSchema.parse(scoped)\n },\n response: ({ result }) => ({\n invoiceId: result?.invoiceId ?? null,\n invoiceLineId: result?.invoiceLineId ?? null,\n auditId: result?.auditId ?? null,\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingInvoiceLineRemove',\n pluralName: 'BillingInvoiceLineRemoves',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingInvoiceRemoveLineSchema,\n responseSchema: removeLineResponseSchema,\n description:\n 'Removes one line from a draft invoice (hard delete \u2014 the line ' +\n 'never existed as far as future readers are concerned). Writes ' +\n 'a `BillingDraftInvoiceEdit` audit row (action=`line_removed`, ' +\n '`after_json=null`) carrying the before-snapshot for forensics. ' +\n 'Recomputes invoice totals. Refuses non-draft invoices.',\n },\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AACjC,SAAS,sCAAsC;AAC/C,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAE/B,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,4BAA4B,EAAE;AAC7E;AAEO,MAAM,WAAW;AAExB,MAAM,2BAA2B,EAAE,OAAO;AAAA,EACxC,WAAW,EAAE,OAAO,EAAE,KAAK;AAAA,EAC3B,eAAe,EAAE,OAAO,EAAE,KAAK;AAAA,EAC/B,SAAS,EAAE,OAAO,EAAE,KAAK;AAC3B,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,IACjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,+BAA+B,MAAM,MAAM;AAAA,MACpD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,WAAW,QAAQ,aAAa;AAAA,QAChC,eAAe,QAAQ,iBAAiB;AAAA,QACxC,SAAS,QAAQ,WAAW;AAAA,MAC9B;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAKJ;AACF,CAAC;",
6
6
  "names": []
7
7
  }
@@ -1,19 +1,11 @@
1
1
  import { NextResponse } from "next/server";
2
- import { z } from "zod";
3
2
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
3
  import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { billingInvoiceListQuerySchema } from "../../data/validators.js";
5
+ import { daysOverdueSql, isoDay, overdueSql } from "../../lib/overdue.js";
5
6
  const metadata = {
6
7
  GET: { requireAuth: true, requireFeatures: ["billing.invoice.view"] }
7
8
  };
8
- const querySchema = z.object({
9
- page: z.coerce.number().int().min(1).default(1),
10
- pageSize: z.coerce.number().int().min(1).max(100).default(50),
11
- status: z.enum(["draft", "posted", "paid", "void"]).optional(),
12
- billRunId: z.string().uuid().optional(),
13
- billAccountId: z.string().uuid().optional(),
14
- testRun: z.coerce.boolean().optional(),
15
- search: z.string().optional()
16
- }).passthrough();
17
9
  async function GET(req) {
18
10
  try {
19
11
  const container = await createRequestContainer();
@@ -29,56 +21,63 @@ async function GET(req) {
29
21
  );
30
22
  }
31
23
  const url = new URL(req.url);
32
- const query = querySchema.parse(
33
- Object.fromEntries(url.searchParams.entries())
34
- );
24
+ const query = billingInvoiceListQuerySchema.parse(Object.fromEntries(url.searchParams.entries()));
35
25
  const em = container.resolve("em").fork();
36
- const whereClauses = [
37
- "tenant_id = ?",
38
- "organization_id = ?",
39
- "deleted_at IS NULL",
40
- "jsonb_exists(metadata, 'bill_run_id')"
41
- ];
26
+ const whereClauses = ["i.tenant_id = ?", "i.organization_id = ?", "i.deleted_at IS NULL"];
42
27
  const params = [auth.tenantId, organizationId];
43
- if (query.status) {
44
- whereClauses.push("status = ?");
45
- params.push(query.status);
28
+ const filters = [
29
+ [query.id, "i.id = ?"],
30
+ [query.status, "i.status = ?"],
31
+ [query.documentType, "i.document_type = ?"],
32
+ [query.billRunId, "i.bill_run_id = ?"],
33
+ [query.billAccountId, "i.bill_account_id = ?"],
34
+ [query.testRun, "i.test_run = ?"]
35
+ ];
36
+ for (const [value, clause] of filters) {
37
+ if (value === void 0 || value === null || value === "") continue;
38
+ whereClauses.push(clause);
39
+ params.push(value);
46
40
  }
47
- if (query.billRunId) {
48
- whereClauses.push("metadata->>'bill_run_id' = ?");
49
- params.push(query.billRunId);
41
+ const today = isoDay(/* @__PURE__ */ new Date());
42
+ if (query.overdue === true) {
43
+ whereClauses.push(overdueSql("i"));
44
+ params.push(today);
45
+ } else if (query.overdue === false) {
46
+ whereClauses.push(`NOT COALESCE(${overdueSql("i")}, false)`);
47
+ params.push(today);
50
48
  }
51
- if (query.billAccountId) {
52
- whereClauses.push("metadata->>'bill_account_id' = ?");
53
- params.push(query.billAccountId);
49
+ if (query.dueFrom) {
50
+ whereClauses.push("i.due_date >= ?::date");
51
+ params.push(query.dueFrom);
54
52
  }
55
- if (typeof query.testRun === "boolean") {
56
- if (query.testRun) {
57
- whereClauses.push("metadata->>'test_run' = 'true'");
58
- } else {
59
- whereClauses.push("(metadata->>'test_run' IS NULL OR metadata->>'test_run' != 'true')");
60
- }
53
+ if (query.dueTo) {
54
+ whereClauses.push("i.due_date <= ?::date");
55
+ params.push(query.dueTo);
61
56
  }
62
- if (typeof query.search === "string" && query.search.trim()) {
63
- whereClauses.push("invoice_number ILIKE ?");
64
- params.push(`%${query.search.trim().replace(/[%_]/g, (m) => `\\${m}`)}%`);
57
+ if (query.search) {
58
+ whereClauses.push("i.number ILIKE ?");
59
+ params.push(`%${query.search.replace(/[%_\\]/g, (m) => `\\${m}`)}%`);
65
60
  }
66
61
  const whereSql = whereClauses.join(" AND ");
67
62
  const countRows = await em.execute(
68
- `SELECT COUNT(*)::int AS total FROM sales_invoices WHERE ${whereSql}`,
63
+ `SELECT COUNT(*)::int AS total FROM billing_invoices i WHERE ${whereSql}`,
69
64
  params
70
65
  );
71
66
  const total = countRows?.[0]?.total ?? 0;
72
67
  const offset = (query.page - 1) * query.pageSize;
73
68
  const itemRows = await em.execute(
74
- `SELECT id, invoice_number, status, currency_code, issue_date, due_date,
75
- subtotal_net_amount, grand_total_net_amount, grand_total_gross_amount,
76
- paid_total_amount, outstanding_amount, metadata, created_at, updated_at
77
- FROM sales_invoices
69
+ `SELECT i.id, i.number, i.document_type, i.status, i.test_run, i.bill_account_id,
70
+ a.name AS bill_account_name, i.bill_run_id, i.bill_date, i.period_start, i.period_end,
71
+ i.issue_date, i.due_date, i.currency_code, i.subtotal_net, i.tax_total, i.total_gross,
72
+ i.paid_total, i.credited_total, i.outstanding, i.corrects_invoice_id, i.corrects_invoice_number, i.topup_id,
73
+ ${daysOverdueSql("i")}::int AS days_overdue,
74
+ i.created_at, i.updated_at
75
+ FROM billing_invoices i
76
+ LEFT JOIN billing_accounts a ON a.id = i.bill_account_id AND a.tenant_id = i.tenant_id
78
77
  WHERE ${whereSql}
79
- ORDER BY created_at DESC
78
+ ORDER BY i.created_at DESC
80
79
  LIMIT ? OFFSET ?`,
81
- [...params, query.pageSize, offset]
80
+ [today, today, ...params, query.pageSize, offset]
82
81
  );
83
82
  return NextResponse.json({
84
83
  items: itemRows,
@@ -96,8 +95,8 @@ const openApi = {
96
95
  methods: {
97
96
  GET: {
98
97
  tags: ["Billing"],
99
- summary: "List billing-managed invoices",
100
- description: "Returns a paginated list of `core/sales` invoices that the billing engine emitted (matched via `metadata->>'bill_run_id'` presence). Supports status / billRunId / billAccountId / testRun / invoice_number search filters."
98
+ summary: "List billing documents",
99
+ description: "Paginated billing documents (invoices, credit / debit memos, top-up receipts) from `billing_invoices`. Filters: status, documentType, billRunId, billAccountId, testRun (`true`/`false`), id, number search, `overdue` (`true`/`false` \u2014 posted or partially paid, past due, outstanding > 0), `dueFrom` / `dueTo` (YYYY-MM-DD). Each row adds `days_overdue`. Fields use billing names: `number`, `document_type`, `total_gross`, `outstanding`, `bill_date`, `period_start/end`, `bill_account_name`, \u2026 (0.21.0 renamed them from the core-sales names \u2014 see MIGRATION.md)."
101
100
  }
102
101
  }
103
102
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/billing/api/invoices/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\n\n/**\n * `GET /api/billing/invoices`\n *\n * Lists `core/sales` invoices that the billing engine emitted (i.e.\n * carry `metadata.bill_run_id`). Hand-rolled rather than wrapped via\n * `makeCrudRoute` because the filter predicate is a JSONB\n * `metadata->>'bill_run_id' IS NOT NULL` check that doesn't map\n * cleanly onto the factory's `where`-shaped filter API, and the\n * entity itself is owned by `core/sales` \u2014 keeping the surface\n * read-only and hand-rolled avoids surprising the cross-module\n * reviewer.\n *\n * Filters:\n * - `status` \u2014 draft / posted / paid / void\n * - `billRunId` \u2014 exact match on `metadata->>'bill_run_id'`\n * - `billAccountId` \u2014 exact match on `metadata->>'bill_account_id'`\n * - `testRun=true|false` \u2014 flips the `metadata->>'test_run'` filter\n * - `search` \u2014 substring on invoice_number\n *\n * Sort: started_at DESC (most recent first). Pagination: standard\n * page / pageSize (capped at 100).\n */\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['billing.invoice.view'] },\n}\n\nconst querySchema = z\n .object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(50),\n status: z.enum(['draft', 'posted', 'paid', 'void']).optional(),\n billRunId: z.string().uuid().optional(),\n billAccountId: z.string().uuid().optional(),\n testRun: z.coerce.boolean().optional(),\n search: z.string().optional(),\n })\n .passthrough()\n\ntype BillingInvoiceRow = {\n id: string\n invoice_number: string\n status: string | null\n currency_code: string\n issue_date: string | null\n due_date: string | null\n subtotal_net_amount: string\n grand_total_net_amount: string\n grand_total_gross_amount: string\n paid_total_amount: string\n outstanding_amount: string\n metadata: Record<string, unknown> | null\n created_at: string\n updated_at: string\n}\n\nexport async function GET(req: Request) {\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(\n { error: 'Organization scope required' },\n { status: 403 },\n )\n }\n\n const url = new URL(req.url)\n const query = querySchema.parse(\n Object.fromEntries(url.searchParams.entries()),\n )\n\n const em = (container.resolve('em') as EntityManager).fork()\n\n // Build the predicate. `jsonb_exists(metadata, 'bill_run_id')` is\n // the function form of the JSONB `?` \"key exists\" operator \u2014 the\n // operator form cannot be used here because `em.execute` treats a\n // literal `?` as a positional parameter placeholder. Keeps us\n // narrow to billing-managed invoices, never bleeding into invoices\n // written by other modules.\n const whereClauses: string[] = [\n 'tenant_id = ?',\n 'organization_id = ?',\n 'deleted_at IS NULL',\n \"jsonb_exists(metadata, 'bill_run_id')\",\n ]\n const params: unknown[] = [auth.tenantId, organizationId]\n\n if (query.status) {\n whereClauses.push('status = ?')\n params.push(query.status)\n }\n if (query.billRunId) {\n whereClauses.push(\"metadata->>'bill_run_id' = ?\")\n params.push(query.billRunId)\n }\n if (query.billAccountId) {\n whereClauses.push(\"metadata->>'bill_account_id' = ?\")\n params.push(query.billAccountId)\n }\n if (typeof query.testRun === 'boolean') {\n if (query.testRun) {\n whereClauses.push(\"metadata->>'test_run' = 'true'\")\n } else {\n whereClauses.push(\"(metadata->>'test_run' IS NULL OR metadata->>'test_run' != 'true')\")\n }\n }\n if (typeof query.search === 'string' && query.search.trim()) {\n whereClauses.push('invoice_number ILIKE ?')\n params.push(`%${query.search.trim().replace(/[%_]/g, (m) => `\\\\${m}`)}%`)\n }\n\n const whereSql = whereClauses.join(' AND ')\n const countRows = (await em.execute(\n `SELECT COUNT(*)::int AS total FROM sales_invoices WHERE ${whereSql}`,\n params,\n )) as unknown as Array<{ total: number }>\n const total = countRows?.[0]?.total ?? 0\n\n const offset = (query.page - 1) * query.pageSize\n const itemRows = (await em.execute(\n `SELECT id, invoice_number, status, currency_code, issue_date, due_date,\n subtotal_net_amount, grand_total_net_amount, grand_total_gross_amount,\n paid_total_amount, outstanding_amount, metadata, created_at, updated_at\n FROM sales_invoices\n WHERE ${whereSql}\n ORDER BY created_at DESC\n LIMIT ? OFFSET ?`,\n [...params, query.pageSize, offset],\n )) as unknown as BillingInvoiceRow[]\n\n return NextResponse.json({\n items: itemRows,\n total,\n page: query.page,\n pageSize: query.pageSize,\n totalPages: total === 0 ? 1 : Math.ceil(total / query.pageSize),\n })\n } catch (err) {\n console.error('[billing/invoices.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: 'List billing-managed invoices',\n description:\n 'Returns a paginated list of `core/sales` invoices that the ' +\n 'billing engine emitted (matched via `metadata->>\\'bill_run_id\\'` ' +\n 'presence). Supports status / billRunId / billAccountId / testRun ' +\n '/ invoice_number search filters.',\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 { billingInvoiceListQuerySchema } from '../../data/validators'\nimport { daysOverdueSql, isoDay, overdueSql } from '../../lib/overdue'\n\n/**\n * `GET /api/billing/invoices` \u2014 paginated list of billing documents\n * (Spec 0: billing-owned `billing_invoices`, never core `sales`).\n *\n * Filters (all on indexed columns): `status`, `documentType`, `billRunId`,\n * `billAccountId`, `testRun` (`true` / `false`), `id`, `search` (number).\n * Spec B: `overdue=true` (computed, B-D6 \u2014 see `lib/overdue.ts`), `dueFrom` /\n * `dueTo` (due-date window). Each row carries `bill_account_name` (joined)\n * and `days_overdue` (0 when not overdue).\n *\n * Hand-written SQL instead of the CRUD factory: documents are written by the\n * bill-run engine without query-index side effects, so the factory's\n * query-engine path would not see them. Sort: `created_at DESC`.\n */\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['billing.invoice.view'] },\n}\n\ntype BillingInvoiceRow = {\n id: string\n number: string\n document_type: string\n status: string\n test_run: boolean\n bill_account_id: string\n bill_account_name: string | null\n bill_run_id: string | null\n bill_date: string | null\n period_start: string | null\n period_end: string | null\n issue_date: string | null\n due_date: string | null\n currency_code: string\n subtotal_net: string\n tax_total: string\n total_gross: string\n paid_total: string\n credited_total: string\n outstanding: string\n days_overdue: number\n corrects_invoice_id: string | null\n topup_id: string | null\n created_at: string\n updated_at: string\n}\n\nexport async function GET(req: Request) {\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(\n { error: 'Organization scope required' },\n { status: 403 },\n )\n }\n\n const url = new URL(req.url)\n const query = billingInvoiceListQuerySchema.parse(Object.fromEntries(url.searchParams.entries()))\n\n const em = (container.resolve('em') as EntityManager).fork()\n\n const whereClauses: string[] = ['i.tenant_id = ?', 'i.organization_id = ?', 'i.deleted_at IS NULL']\n const params: unknown[] = [auth.tenantId, organizationId]\n const filters: Array<[unknown, string]> = [\n [query.id, 'i.id = ?'],\n [query.status, 'i.status = ?'],\n [query.documentType, 'i.document_type = ?'],\n [query.billRunId, 'i.bill_run_id = ?'],\n [query.billAccountId, 'i.bill_account_id = ?'],\n [query.testRun, 'i.test_run = ?'],\n ]\n for (const [value, clause] of filters) {\n if (value === undefined || value === null || value === '') continue\n whereClauses.push(clause)\n params.push(value)\n }\n const today = isoDay(new Date())\n if (query.overdue === true) {\n whereClauses.push(overdueSql('i'))\n params.push(today)\n } else if (query.overdue === false) {\n whereClauses.push(`NOT COALESCE(${overdueSql(\"i\")}, false)`)\n params.push(today)\n }\n if (query.dueFrom) {\n whereClauses.push('i.due_date >= ?::date')\n params.push(query.dueFrom)\n }\n if (query.dueTo) {\n whereClauses.push('i.due_date <= ?::date')\n params.push(query.dueTo)\n }\n if (query.search) {\n whereClauses.push('i.number ILIKE ?')\n params.push(`%${query.search.replace(/[%_\\\\]/g, (m) => `\\\\${m}`)}%`)\n }\n\n const whereSql = whereClauses.join(' AND ')\n const countRows = (await em.execute(\n `SELECT COUNT(*)::int AS total FROM billing_invoices i WHERE ${whereSql}`,\n params,\n )) as unknown as Array<{ total: number }>\n const total = countRows?.[0]?.total ?? 0\n\n const offset = (query.page - 1) * query.pageSize\n const itemRows = (await em.execute(\n `SELECT i.id, i.number, i.document_type, i.status, i.test_run, i.bill_account_id,\n a.name AS bill_account_name, i.bill_run_id, i.bill_date, i.period_start, i.period_end,\n i.issue_date, i.due_date, i.currency_code, i.subtotal_net, i.tax_total, i.total_gross,\n i.paid_total, i.credited_total, i.outstanding, i.corrects_invoice_id, i.corrects_invoice_number, i.topup_id,\n ${daysOverdueSql('i')}::int AS days_overdue,\n i.created_at, i.updated_at\n FROM billing_invoices i\n LEFT JOIN billing_accounts a ON a.id = i.bill_account_id AND a.tenant_id = i.tenant_id\n WHERE ${whereSql}\n ORDER BY i.created_at DESC\n LIMIT ? OFFSET ?`,\n [today, today, ...params, query.pageSize, offset],\n )) as unknown as BillingInvoiceRow[]\n\n return NextResponse.json({\n items: itemRows,\n total,\n page: query.page,\n pageSize: query.pageSize,\n totalPages: total === 0 ? 1 : Math.ceil(total / query.pageSize),\n })\n } catch (err) {\n console.error('[billing/invoices.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: 'List billing documents',\n description:\n 'Paginated billing documents (invoices, credit / debit memos, top-up receipts) from ' +\n '`billing_invoices`. Filters: status, documentType, billRunId, billAccountId, testRun ' +\n '(`true`/`false`), id, number search, `overdue` (`true`/`false` \u2014 posted or partially paid, ' +\n 'past due, outstanding > 0), `dueFrom` / `dueTo` (YYYY-MM-DD). Each row adds `days_overdue`. ' +\n 'Fields use billing names: `number`, `document_type`, ' +\n '`total_gross`, `outstanding`, `bill_date`, `period_start/end`, `bill_account_name`, \u2026 ' +\n '(0.21.0 renamed them from the core-sales names \u2014 see MIGRATION.md).',\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -63,7 +63,7 @@ const openApi = createBillingCrudOpenApi({
63
63
  create: {
64
64
  schema: billingItemBulkCreateSchema,
65
65
  responseSchema: bulkResponseSchema,
66
- description: "Batch-creates up to 500 Billing Items under one scope envelope. Idempotent per `sourceRef` (one batched existence check). Returns `{ created, deduplicated, items: [{ sourceRef, id, deduplicated }] }`. Always HTTP 200 \u2014 duplicates are reported in the body, never an error."
66
+ description: "Batch-creates up to 500 Billing Items under one scope envelope. Idempotent per `sourceRef` (one batched existence check). Returns `{ created, deduplicated, items: [{ sourceRef, id, deduplicated }] }`. Always HTTP 200 \u2014 duplicates are reported in the body, never an error. Entries accept `chargeTiming` / `prorate` (recurring only; defaults `in_advance` / true)."
67
67
  }
68
68
  });
69
69
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/billing/api/items/bulk/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 { BillingItem } from '../../../data/entities'\nimport { billingEntityIds } from '../../../data/entityIds'\nimport { billingItemBulkCreateSchema } from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\n\n/**\n * `POST /api/billing/items/bulk` \u2014 batch-create Billing Items.\n *\n * One scope envelope, up to 500 item payloads. Idempotent per\n * `source_ref` with a *single* batched existence check, then a\n * single flush. The CPQ connector's subscription-activated /\n * -amended / -renewed subscribers are the primary callers \u2014 bulk\n * collapses what used to be one `billing.items.create` round-trip\n * per charge.\n */\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.item.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst bulkResultEntrySchema = z.object({\n sourceRef: z.string().nullable(),\n id: z.string().uuid(),\n deduplicated: z.boolean(),\n})\n\nconst bulkResponseSchema = z.object({\n created: z.number().int().nonnegative(),\n deduplicated: z.number().int().nonnegative(),\n items: z.array(bulkResultEntrySchema),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: BillingItem,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n entityId: billingEntityIds.item,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.items.bulk_create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingItemBulkCreateSchema.parse(scoped)\n },\n response: ({ result }) => ({\n created: result?.created ?? 0,\n deduplicated: result?.deduplicated ?? 0,\n items: result?.items ?? [],\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingItemBulkCreate',\n pluralName: 'BillingItemBulkCreates',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingItemBulkCreateSchema,\n responseSchema: bulkResponseSchema,\n description:\n 'Batch-creates up to 500 Billing Items under one scope envelope. ' +\n 'Idempotent per `sourceRef` (one batched existence check). ' +\n 'Returns `{ created, deduplicated, items: [{ sourceRef, id, ' +\n 'deduplicated }] }`. Always HTTP 200 \u2014 duplicates are reported ' +\n 'in the body, never an error.',\n },\n})\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB;AACjC,SAAS,mCAAmC;AAC5C,SAAS,gCAAgC;AAazC,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AACtE;AAEO,MAAM,WAAW;AAExB,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,cAAc,EAAE,QAAQ;AAC1B,CAAC;AAED,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACtC,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EAC3C,OAAO,EAAE,MAAM,qBAAqB;AACtC,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,IACjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,4BAA4B,MAAM,MAAM;AAAA,MACjD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,SAAS,QAAQ,WAAW;AAAA,QAC5B,cAAc,QAAQ,gBAAgB;AAAA,QACtC,OAAO,QAAQ,SAAS,CAAC;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAKJ;AACF,CAAC;",
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 { BillingItem } from '../../../data/entities'\nimport { billingEntityIds } from '../../../data/entityIds'\nimport { billingItemBulkCreateSchema } from '../../../data/validators'\nimport { createBillingCrudOpenApi } from '../../openapi'\n\n/**\n * `POST /api/billing/items/bulk` \u2014 batch-create Billing Items.\n *\n * One scope envelope, up to 500 item payloads. Idempotent per\n * `source_ref` with a *single* batched existence check, then a\n * single flush. The CPQ connector's subscription-activated /\n * -amended / -renewed subscribers are the primary callers \u2014 bulk\n * collapses what used to be one `billing.items.create` round-trip\n * per charge.\n */\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.item.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst bulkResultEntrySchema = z.object({\n sourceRef: z.string().nullable(),\n id: z.string().uuid(),\n deduplicated: z.boolean(),\n})\n\nconst bulkResponseSchema = z.object({\n created: z.number().int().nonnegative(),\n deduplicated: z.number().int().nonnegative(),\n items: z.array(bulkResultEntrySchema),\n})\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: BillingItem,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: z.object({}).passthrough(),\n entityId: billingEntityIds.item,\n fields: ['id'],\n buildFilters: async () => ({}),\n },\n actions: {\n create: {\n commandId: 'billing.items.bulk_create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n return billingItemBulkCreateSchema.parse(scoped)\n },\n response: ({ result }) => ({\n created: result?.created ?? 0,\n deduplicated: result?.deduplicated ?? 0,\n items: result?.items ?? [],\n }),\n status: 200,\n },\n },\n})\n\nexport const POST = crud.POST\n\nexport const openApi = createBillingCrudOpenApi({\n resourceName: 'BillingItemBulkCreate',\n pluralName: 'BillingItemBulkCreates',\n querySchema: z.object({}).passthrough(),\n listResponseSchema: z.object({ items: z.array(z.unknown()) }),\n create: {\n schema: billingItemBulkCreateSchema,\n responseSchema: bulkResponseSchema,\n description:\n 'Batch-creates up to 500 Billing Items under one scope envelope. ' +\n 'Idempotent per `sourceRef` (one batched existence check). ' +\n 'Returns `{ created, deduplicated, items: [{ sourceRef, id, ' +\n 'deduplicated }] }`. Always HTTP 200 \u2014 duplicates are reported ' +\n 'in the body, never an error. Entries accept `chargeTiming` / `prorate` ' +\n '(recurring only; defaults `in_advance` / true).',\n },\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB;AACjC,SAAS,mCAAmC;AAC5C,SAAS,gCAAgC;AAazC,MAAM,gBAAgB;AAAA,EACpB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AACtE;AAEO,MAAM,WAAW;AAExB,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,cAAc,EAAE,QAAQ;AAC1B,CAAC;AAED,MAAM,qBAAqB,EAAE,OAAO;AAAA,EAClC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EACtC,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;AAAA,EAC3C,OAAO,EAAE,MAAM,qBAAqB;AACtC,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,IACjC,UAAU,iBAAiB;AAAA,IAC3B,QAAQ,CAAC,IAAI;AAAA,IACb,cAAc,aAAa,CAAC;AAAA,EAC9B;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,eAAO,4BAA4B,MAAM,MAAM;AAAA,MACjD;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,SAAS,QAAQ,WAAW;AAAA,QAC5B,cAAc,QAAQ,gBAAgB;AAAA,QACtC,OAAO,QAAQ,SAAS,CAAC;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AACF,CAAC;AAEM,MAAM,OAAO,KAAK;AAElB,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACtC,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC5D,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aACE;AAAA,EAMJ;AACF,CAAC;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import { runCommandRoute } from "../../../lib/commandRoute.js";
3
+ const metadata = {
4
+ POST: { requireAuth: true, requireFeatures: ["billing.item.manage"] }
5
+ };
6
+ const bodySchema = z.object({
7
+ id: z.string().uuid(),
8
+ effectiveDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
9
+ unitPrice: z.coerce.number().finite().nonnegative()
10
+ });
11
+ const responseSchema = z.object({ previousItemId: z.string().uuid(), newItemId: z.string().uuid() });
12
+ async function POST(req) {
13
+ return runCommandRoute(req, {
14
+ commandId: "billing.items.change_price",
15
+ bodySchema,
16
+ resourceKind: "billing.item",
17
+ resourceId: (body) => body.id,
18
+ logLabel: "billing/items/change-price.POST"
19
+ });
20
+ }
21
+ const openApi = {
22
+ methods: {
23
+ POST: {
24
+ tags: ["Billing"],
25
+ summary: "Change a recurring item's price from a date",
26
+ description: "Ends the item the day before `effectiveDate` and creates a copy with the new `unitPrice` (on the item price basis) starting on `effectiveDate`. Already-billed days after the change are credited / re-charged by the Bill Run per the item's proration setting, so nothing is billed twice.",
27
+ requestBody: { schema: bodySchema },
28
+ responses: [{ status: 200, description: "Both item ids", schema: responseSchema }],
29
+ errors: [
30
+ { status: 404, description: "Item not found" },
31
+ {
32
+ status: 422,
33
+ description: "`billing.item.price_change_recurring_only` / `_inactive` / `_before_start` (must be after the start date) / `_after_end`"
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ };
39
+ export {
40
+ POST,
41
+ metadata,
42
+ openApi
43
+ };
44
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/billing/api/items/change-price/route.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { runCommandRoute } from '../../../lib/commandRoute'\n\n/**\n * `POST /api/billing/items/change-price` \u2014 change a recurring item's price\n * from a date on: the item ends the day before `effectiveDate` and a copy with\n * `unitPrice` starts on it (same account, description, timing, proration,\n * VAT, price basis, references and end date). Returns both item ids.\n */\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['billing.item.manage'] },\n}\n\nconst bodySchema = z.object({\n id: z.string().uuid(),\n effectiveDate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n unitPrice: z.coerce.number().finite().nonnegative(),\n})\n\nconst responseSchema = z.object({ previousItemId: z.string().uuid(), newItemId: z.string().uuid() })\n\nexport async function POST(req: Request) {\n return runCommandRoute(req, {\n commandId: 'billing.items.change_price',\n bodySchema,\n resourceKind: 'billing.item',\n resourceId: (body) => body.id,\n logLabel: 'billing/items/change-price.POST',\n })\n}\n\nexport const openApi = {\n methods: {\n POST: {\n tags: ['Billing'],\n summary: \"Change a recurring item's price from a date\",\n description:\n 'Ends the item the day before `effectiveDate` and creates a copy with the new `unitPrice` (on the item price basis) ' +\n 'starting on `effectiveDate`. Already-billed days after the change are credited / re-charged by the Bill Run per the ' +\n \"item's proration setting, so nothing is billed twice.\",\n requestBody: { schema: bodySchema },\n responses: [{ status: 200, description: 'Both item ids', schema: responseSchema }],\n errors: [\n { status: 404, description: 'Item not found' },\n {\n status: 422,\n description:\n '`billing.item.price_change_recurring_only` / `_inactive` / `_before_start` (must be after the start date) / `_after_end`',\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,uBAAuB;AASzB,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AACtE;AAEA,MAAM,aAAa,EAAE,OAAO;AAAA,EAC1B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,eAAe,EAAE,OAAO,EAAE,MAAM,qBAAqB;AAAA,EACrD,WAAW,EAAE,OAAO,OAAO,EAAE,OAAO,EAAE,YAAY;AACpD,CAAC;AAED,MAAM,iBAAiB,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,KAAK,GAAG,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAEnG,eAAsB,KAAK,KAAc;AACvC,SAAO,gBAAgB,KAAK;AAAA,IAC1B,WAAW;AAAA,IACX;AAAA,IACA,cAAc;AAAA,IACd,YAAY,CAAC,SAAS,KAAK;AAAA,IAC3B,UAAU;AAAA,EACZ,CAAC;AACH;AAEO,MAAM,UAAU;AAAA,EACrB,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,MAAM,CAAC,SAAS;AAAA,MAChB,SAAS;AAAA,MACT,aACE;AAAA,MAGF,aAAa,EAAE,QAAQ,WAAW;AAAA,MAClC,WAAW,CAAC,EAAE,QAAQ,KAAK,aAAa,iBAAiB,QAAQ,eAAe,CAAC;AAAA,MACjF,QAAQ;AAAA,QACN,EAAE,QAAQ,KAAK,aAAa,iBAAiB;AAAA,QAC7C;AAAA,UACE,QAAQ;AAAA,UACR,aACE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -25,6 +25,9 @@ const billingItemListItemSchema = z.object({
25
25
  id: z.string().uuid(),
26
26
  bill_account_id: z.string().uuid(),
27
27
  bill_account_name: z.string().nullable(),
28
+ /** Spec F (F2): the owning account's currency and mode, for the form. */
29
+ bill_account_currency: z.string().nullable(),
30
+ bill_account_mode: z.enum(["postpaid", "prepaid"]).nullable(),
28
31
  type: z.string(),
29
32
  bill_start_date: z.string(),
30
33
  bill_end_date: z.string().nullable(),
@@ -36,6 +39,10 @@ const billingItemListItemSchema = z.object({
36
39
  source_ref: z.string().nullable(),
37
40
  currency_mismatch: z.boolean(),
38
41
  billed_to_date: z.string().nullable(),
42
+ charge_timing: z.enum(["in_advance", "in_arrears"]),
43
+ prorate: z.boolean(),
44
+ tax_rate: z.union([z.string(), z.number()]),
45
+ price_basis: z.enum(["net", "gross"]),
39
46
  is_active: z.boolean(),
40
47
  organization_id: z.string().uuid(),
41
48
  tenant_id: z.string().uuid(),
@@ -73,6 +80,10 @@ const crud = makeCrudRoute({
73
80
  "source_ref",
74
81
  "currency_mismatch",
75
82
  "billed_to_date",
83
+ "charge_timing",
84
+ "prorate",
85
+ "tax_rate",
86
+ "price_basis",
76
87
  "is_active",
77
88
  "organization_id",
78
89
  "tenant_id",
@@ -169,7 +180,7 @@ const crud = makeCrudRoute({
169
180
  },
170
181
  hooks: {
171
182
  // Items reference their account by FK id only; the list + detail UI
172
- // wants the human-readable account name. One bounded query
183
+ // wants the account name, currency and mode. One bounded query
173
184
  // (≤ pageSize ids) resolves it after the page is fetched.
174
185
  afterList: async (payload, ctx) => {
175
186
  const items = payload?.items ?? [];
@@ -181,11 +192,12 @@ const crud = makeCrudRoute({
181
192
  if (accountIds.length === 0) return;
182
193
  const em = ctx.container.resolve("em");
183
194
  const accounts = await em.find(BillingAccount, { id: { $in: accountIds } });
184
- const nameById = new Map(
185
- accounts.map((a) => [String(a.id), a.name])
186
- );
195
+ const byId = new Map(accounts.map((a) => [String(a.id), a]));
187
196
  for (const row of items) {
188
- row.bill_account_name = nameById.get(String(row.bill_account_id)) ?? null;
197
+ const account = byId.get(String(row.bill_account_id));
198
+ row.bill_account_name = account?.name ?? null;
199
+ row.bill_account_currency = account?.currencyCode ?? null;
200
+ row.bill_account_mode = account?.billingMode ?? null;
189
201
  }
190
202
  }
191
203
  }
@@ -200,12 +212,12 @@ const openApi = createBillingCrudOpenApi({
200
212
  create: {
201
213
  schema: billingItemCreateSchema,
202
214
  responseSchema: itemCreateResponseSchema,
203
- description: "Creates a billing item. Idempotent via `sourceRef`: re-POSTing the same `(tenantId, billAccountId, sourceRef)` returns the existing row with `deduplicated: true` instead of creating a duplicate. The unique index on `(tenant_id, bill_account_id, source_ref)` is the correctness gate."
215
+ description: "Creates a billing item. Idempotent via `sourceRef`: re-POSTing the same `(tenantId, billAccountId, sourceRef)` returns the existing row with `deduplicated: true` instead of creating a duplicate. The unique index on `(tenant_id, bill_account_id, source_ref)` is the correctness gate. Recurring items accept `chargeTiming` (`in_advance` default | `in_arrears`) and `prorate` (default true); other types reject non-default values (400)."
204
216
  },
205
217
  update: {
206
218
  schema: billingItemUpdateSchema,
207
219
  responseSchema: defaultOkResponseSchema,
208
- description: "Updates a billing item. `type` and `sourceRef` are immutable \u2014 to change either, soft-delete the item and create a new one."
220
+ description: "Updates a billing item. `type` and `sourceRef` are immutable \u2014 to change either, soft-delete the item and create a new one. `chargeTiming` / `prorate` may change at any time on recurring items \u2014 billing resumes from `billed_to_date`, so no day is billed twice."
209
221
  },
210
222
  del: {
211
223
  schema: z.object({ id: z.string().uuid() }),