@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
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Posting a real run's draft invoices in cursor batches (`billing.runs.post_drafts`).
3
+ */
4
+
5
+ jest.mock('../../events', () => ({ emitBillingEvent: jest.fn(async () => undefined) }))
6
+ jest.mock('../../lib/billRunEngine', () => ({ runBillRun: jest.fn() }))
7
+
8
+ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
9
+ import { postDraftsCommand } from '../../commands/runs'
10
+
11
+ const TENANT = '11111111-1111-4111-8111-111111111111'
12
+ const ORG = '22222222-2222-4222-8222-222222222222'
13
+ const RUN_ID = '33333333-3333-4333-8333-333333333333'
14
+ const A = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'
15
+ const B = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'
16
+ const C = 'cccccccc-cccc-4ccc-8ccc-cccccccccccc'
17
+
18
+ type RunRow = { id: string; dryRun: boolean; testMode: boolean; status: string }
19
+
20
+ function createEnv(opts: { run: RunRow | null; drafts: Array<{ id: string; number: string }>; remaining?: number; failOn?: Record<string, { code: string; error: string }> }) {
21
+ const posted: string[] = []
22
+ const executed: Array<{ sql: string; params: unknown[] }> = []
23
+ const em = {
24
+ findOne: jest.fn(async () => opts.run),
25
+ execute: jest.fn(async (sql: string, params: unknown[] = []) => {
26
+ executed.push({ sql, params })
27
+ if (sql.includes('COUNT(*)')) return [{ remaining: opts.remaining ?? 0 }]
28
+ return opts.drafts
29
+ }),
30
+ fork: jest.fn((): unknown => em),
31
+ }
32
+ const commandBus = {
33
+ execute: jest.fn(async (_id: string, args: { input: { invoiceId: string } }) => {
34
+ const failure = opts.failOn?.[args.input.invoiceId]
35
+ if (failure) throw new CrudHttpError(409, failure)
36
+ posted.push(args.input.invoiceId)
37
+ return { result: { status: 'posted' } }
38
+ }),
39
+ }
40
+ const ctx = {
41
+ container: { resolve: jest.fn((name: string) => (name === 'commandBus' ? commandBus : em)) },
42
+ auth: { tenantId: TENANT, orgId: ORG },
43
+ selectedOrganizationId: ORG,
44
+ organizationIds: [ORG],
45
+ organizationScope: null,
46
+ }
47
+ return { ctx, posted, executed, commandBus }
48
+ }
49
+
50
+ const realRun: RunRow = { id: RUN_ID, dryRun: false, testMode: false, status: 'completed' }
51
+ const input = { tenantId: TENANT, organizationId: ORG, billRunId: RUN_ID }
52
+
53
+ describe('postDraftsCommand', () => {
54
+ it('posts each draft through billing.invoices.post and returns the cursor + remaining', async () => {
55
+ const env = createEnv({ run: realRun, drafts: [{ id: A, number: 'DRAFT-A' }, { id: B, number: 'DRAFT-B' }], remaining: 5 })
56
+ const result = await postDraftsCommand.execute({ ...input, limit: 2 }, env.ctx as never)
57
+ expect(env.posted).toEqual([A, B])
58
+ expect(env.commandBus.execute).toHaveBeenCalledWith('billing.invoices.post', expect.objectContaining({
59
+ input: { tenantId: TENANT, organizationId: ORG, invoiceId: A },
60
+ }))
61
+ expect(result).toEqual({ posted: 2, failed: [], nextCursor: B, remaining: 5, stoppedReason: null })
62
+ // Draft selection is scoped, real-only and keyset-paged.
63
+ const select = env.executed[0]
64
+ expect(select.sql).toContain("status = 'draft'")
65
+ expect(select.sql).toContain('test_run = false')
66
+ expect(select.params).toEqual([TENANT, ORG, RUN_ID, null, null, 2])
67
+ })
68
+
69
+ it('reports a failing invoice and carries on with the rest', async () => {
70
+ const env = createEnv({
71
+ run: realRun,
72
+ drafts: [{ id: A, number: 'DRAFT-A' }, { id: B, number: 'DRAFT-B' }, { id: C, number: 'DRAFT-C' }],
73
+ failOn: { [B]: { code: 'billing.invoice.number_conflict', error: 'Number already issued' } },
74
+ })
75
+ const result = await postDraftsCommand.execute(input, env.ctx as never)
76
+ expect(env.posted).toEqual([A, C])
77
+ expect(result.failed).toEqual([
78
+ { invoiceId: B, number: 'DRAFT-B', error: 'Number already issued', code: 'billing.invoice.number_conflict' },
79
+ ])
80
+ expect(result.nextCursor).toBe(C)
81
+ })
82
+
83
+ it('stops at incomplete seller settings and keeps the starting cursor', async () => {
84
+ const env = createEnv({
85
+ run: realRun,
86
+ drafts: [{ id: A, number: 'DRAFT-A' }, { id: B, number: 'DRAFT-B' }],
87
+ remaining: 2,
88
+ failOn: { [A]: { code: 'billing.settings.incomplete', error: 'Fill in the seller details' } },
89
+ })
90
+ const result = await postDraftsCommand.execute(input, env.ctx as never)
91
+ expect(env.commandBus.execute).toHaveBeenCalledTimes(1)
92
+ expect(result).toMatchObject({ posted: 0, stoppedReason: 'billing.settings.incomplete', nextCursor: null, remaining: 2 })
93
+ })
94
+
95
+ it('refuses test / dry runs (422), running runs (409) and unknown runs (404)', async () => {
96
+ await expect(
97
+ postDraftsCommand.execute(input, createEnv({ run: { ...realRun, testMode: true }, drafts: [] }).ctx as never),
98
+ ).rejects.toMatchObject({ status: 422, body: { code: 'billing.run.not_real' } })
99
+ await expect(
100
+ postDraftsCommand.execute(input, createEnv({ run: { ...realRun, status: 'running' }, drafts: [] }).ctx as never),
101
+ ).rejects.toMatchObject({ status: 409 })
102
+ await expect(postDraftsCommand.execute(input, createEnv({ run: null, drafts: [] }).ctx as never)).rejects.toMatchObject({
103
+ status: 404,
104
+ })
105
+ })
106
+ })
@@ -25,6 +25,8 @@ function makeCtx(em: MockEm, gateway: unknown) {
25
25
  return {
26
26
  auth: { sub: '99999999-9999-4999-8999-999999999999', tenantId: TENANT, orgId: ORG },
27
27
  container: {
28
+ // E-D1: the gateway is optional — registered only when one is passed.
29
+ hasRegistration: jest.fn((name: string) => name !== 'paymentGatewayService' || gateway != null),
28
30
  resolve: jest.fn((name: string) => {
29
31
  if (name === 'em') return em
30
32
  if (name === 'dataEngine') return dataEngine
@@ -101,4 +103,13 @@ describe('createTopupCommand', () => {
101
103
  expect(result.redirectUrl).toBe('https://pay.example/prior')
102
104
  expect(gateway.createPaymentSession).not.toHaveBeenCalled()
103
105
  })
106
+
107
+ it('returns 501 gateway_unavailable without persisting when payment_gateways is not installed', async () => {
108
+ const em = createPrepaidMockEm({ account: prepaidAccount(), topup: null })
109
+ await expect(createTopupCommand.execute(baseInput, makeCtx(em, null) as never)).rejects.toMatchObject({
110
+ status: 501,
111
+ body: expect.objectContaining({ code: 'billing.topup.gateway_unavailable' }),
112
+ })
113
+ expect(em.persisted.some((e) => (e as { status?: string }).status === 'pending')).toBe(false)
114
+ })
104
115
  })
@@ -0,0 +1,326 @@
1
+ /**
2
+ * Shipped entity-id registry (`packages/billing/generated/`).
3
+ *
4
+ * A standalone Open Mercato app only registers billing's entity ids if the
5
+ * package ships `generated/entities.ids.generated.ts` — the compiled
6
+ * `dist/.../data/entities.js` uses `export { … }`, which the CLI's class
7
+ * scanner cannot see, so without the registry the app gets
8
+ * `"billing": {}`. Two things then break:
9
+ * - the query-index subscriber rejects every billing write
10
+ * (`QueryIndexScopeError: Query index entity type is not registered`);
11
+ * - `mercato entities install` registers the ids as virtual custom
12
+ * entities and the query engine stops reading the billing tables.
13
+ *
14
+ * These tests pin the registry contents, check it is in sync with its
15
+ * sources, run the real `@open-mercato/cli` generator over the package,
16
+ * and feed the ids through the framework functions that failed, against
17
+ * billing's real MikroORM metadata (discovered without a database) — down
18
+ * to what `billing.accounts.create` hands the real data engine.
19
+ */
20
+
21
+ import { execFileSync } from 'node:child_process'
22
+ import fs from 'node:fs'
23
+ import path from 'node:path'
24
+
25
+ import type { EntityManager } from '@mikro-orm/postgresql'
26
+ import { MikroORM } from '@mikro-orm/postgresql'
27
+ import {
28
+ QueryIndexScopeError,
29
+ resolveQueryIndexSourceMetadata,
30
+ } from '@open-mercato/core/modules/query_index/lib/subscriber-scope'
31
+ import { DefaultDataEngine, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'
32
+ import { registerEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'
33
+ import { resolveRegisteredEntityTableName } from '@open-mercato/shared/lib/query/engine'
34
+ import { isEventDeclared } from '@open-mercato/shared/modules/events'
35
+
36
+ import { createAccountCommand } from '../commands/accounts'
37
+ import '../events'
38
+ import * as billingEntities from '../data/entities'
39
+ import { billingEntityIds } from '../data/entityIds'
40
+ import { E, M } from '../../../../generated/entities.ids.generated'
41
+
42
+ const PACKAGE_ROOT = path.resolve(__dirname, '../../../..')
43
+ const REPO_ROOT = path.resolve(PACKAGE_ROOT, '../..')
44
+ const GENERATED_ROOT = path.join(PACKAGE_ROOT, 'generated')
45
+
46
+ const ALL_IDS = Object.values(billingEntityIds)
47
+ const suffixOf = (id: string) => id.split(':')[1]
48
+
49
+ /** Every billing id → the table the framework must resolve it to. */
50
+ const EXPECTED_TABLES: Record<string, string> = {
51
+ [billingEntityIds.account]: 'billing_accounts',
52
+ [billingEntityIds.item]: 'billing_items',
53
+ [billingEntityIds.usage]: 'billing_account_usage',
54
+ [billingEntityIds.run]: 'billing_runs',
55
+ [billingEntityIds.runOutcome]: 'billing_run_outcomes',
56
+ [billingEntityIds.draftInvoiceEdit]: 'billing_draft_invoice_edits',
57
+ [billingEntityIds.balance]: 'billing_account_balances',
58
+ [billingEntityIds.transaction]: 'billing_account_transactions',
59
+ [billingEntityIds.topup]: 'billing_topups',
60
+ [billingEntityIds.statement]: 'billing_statements',
61
+ [billingEntityIds.invoice]: 'billing_invoices',
62
+ [billingEntityIds.invoiceLine]: 'billing_invoice_lines',
63
+ [billingEntityIds.documentSequence]: 'billing_document_sequences',
64
+ [billingEntityIds.settings]: 'billing_settings',
65
+ [billingEntityIds.payment]: 'billing_payments',
66
+ [billingEntityIds.allocation]: 'billing_allocations',
67
+ [billingEntityIds.paymentImport]: 'billing_payment_imports',
68
+ }
69
+
70
+ /** PascalCase of an id suffix — the class name the framework looks up. */
71
+ const classNameOf = (id: string) =>
72
+ suffixOf(id)
73
+ .split('_')
74
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
75
+ .join('')
76
+
77
+ /** A registry as a real app would have it: core ids present, billing empty (the bug). */
78
+ const REGISTRY_WITHOUT_BILLING = {
79
+ directory: { tenant: 'directory:tenant', organization: 'directory:organization' },
80
+ billing: {},
81
+ }
82
+
83
+ function readShippedColumns(key: string): string[] {
84
+ const file = path.join(GENERATED_ROOT, 'entities', key, 'index.ts')
85
+ const source = fs.readFileSync(file, 'utf8')
86
+ return Array.from(source.matchAll(/^export const \w+ = "([^"]+)"$/gm), (m) => m[1])
87
+ }
88
+
89
+ let orm: MikroORM
90
+ let em: EntityManager
91
+
92
+ beforeAll(() => {
93
+ // Every runtime export of data/entities.ts is an entity class.
94
+ const entityClasses = Object.values(billingEntities)
95
+ // The constructor discovers metadata synchronously; no connection is opened.
96
+ orm = new MikroORM({ entities: entityClasses, dbName: 'billing_entity_registry_test' })
97
+ em = orm.em.fork() as EntityManager
98
+ })
99
+
100
+ afterAll(async () => {
101
+ await orm.close(true)
102
+ })
103
+
104
+ describe('shipped registry — generated/entities.ids.generated.ts', () => {
105
+ it('declares the billing module', () => {
106
+ expect(M).toEqual({ billing: 'billing' })
107
+ })
108
+
109
+ it('lists every billingEntityIds value under its id suffix, and nothing else', () => {
110
+ // The CLI ignores the values and registers `billing:<key>`, so the keys
111
+ // are what matters.
112
+ const expected = Object.fromEntries(ALL_IDS.map((id) => [suffixOf(id), id]))
113
+ expect(E.billing).toEqual(expected)
114
+ expect(Object.keys(E.billing)).toHaveLength(17)
115
+ })
116
+
117
+ it('ships one column file per entity, matching the ORM columns', () => {
118
+ const shippedDirs = fs.readdirSync(path.join(GENERATED_ROOT, 'entities')).sort()
119
+ expect(shippedDirs).toEqual(ALL_IDS.map(suffixOf).sort())
120
+
121
+ for (const id of ALL_IDS) {
122
+ const table = EXPECTED_TABLES[id]
123
+ const meta = orm.getMetadata().getAll()
124
+ const entityMeta = Array.from(meta.values()).find((candidate) => candidate.tableName === table)
125
+ expect(entityMeta).toBeDefined()
126
+ const ormColumns = entityMeta!.props.flatMap((prop) => prop.fieldNames ?? [])
127
+ expect({ id, columns: readShippedColumns(suffixOf(id)).sort() }).toEqual({ id, columns: [...ormColumns].sort() })
128
+ }
129
+ })
130
+
131
+ it('is in sync with data/entityIds.ts and data/entities.ts', () => {
132
+ // Throws (exit 1) and names the stale files when generated/ drifted.
133
+ expect(() =>
134
+ execFileSync(process.execPath, [path.join(PACKAGE_ROOT, 'scripts', 'generate-entity-registry.mjs'), '--check'], {
135
+ cwd: PACKAGE_ROOT,
136
+ stdio: 'pipe',
137
+ timeout: 60_000,
138
+ }),
139
+ ).not.toThrow()
140
+ })
141
+
142
+ it('is what `mercato generate` registers for billing in a standalone app', () => {
143
+ // Runs the real @open-mercato/cli entity-ids generator over the package,
144
+ // resolved the way a standalone app resolves it (dist/modules when built,
145
+ // else src/modules). Without the registry this yields `billing: {}` (dist)
146
+ // or class-derived ids such as `billing:bill_run` (src).
147
+ const script = `
148
+ import fs from 'node:fs'
149
+ import os from 'node:os'
150
+ import path from 'node:path'
151
+ import { createRequire } from 'node:module'
152
+ const [repoRoot] = process.argv.slice(1)
153
+ const require = createRequire(path.join(repoRoot, 'package.json'))
154
+ const { generateEntityIds } = await import(require.resolve('@open-mercato/cli/lib/generators/entity-ids'))
155
+ const pkgRoot = path.join(repoRoot, 'node_modules', '@dainamite', 'billing')
156
+ const distModules = path.join(pkgRoot, 'dist', 'modules')
157
+ const pkgModules = fs.existsSync(distModules) ? distModules : path.join(pkgRoot, 'src', 'modules')
158
+ const out = fs.mkdtempSync(path.join(os.tmpdir(), 'billing-entity-ids-'))
159
+ try {
160
+ await generateEntityIds({
161
+ quiet: true,
162
+ resolver: {
163
+ isMonorepo: () => false,
164
+ getOutputDir: () => out,
165
+ getPackageOutputDir: () => out,
166
+ loadEnabledModules: () => [{ id: 'billing', from: '@dainamite/billing' }],
167
+ getModulePaths: (entry) => ({
168
+ appBase: path.join(repoRoot, 'src', 'modules', entry.id),
169
+ pkgBase: path.join(pkgModules, entry.id),
170
+ }),
171
+ getModuleImportBase: (entry) => ({ appBase: '@/modules/' + entry.id, pkgBase: entry.from + '/modules/' + entry.id }),
172
+ getPackageRoot: (from) => path.join(repoRoot, 'node_modules', from),
173
+ },
174
+ })
175
+ const source = fs.readFileSync(path.join(out, 'entities.ids.generated.ts'), 'utf8')
176
+ const match = source.match(/E = (\\{[\\s\\S]*?\\}) as const/)
177
+ process.stdout.write(match ? match[1] : 'null')
178
+ } finally {
179
+ fs.rmSync(out, { recursive: true, force: true })
180
+ }
181
+ `
182
+ const output = execFileSync(process.execPath, ['--input-type=module', '-e', script, REPO_ROOT], {
183
+ cwd: REPO_ROOT,
184
+ encoding: 'utf8',
185
+ stdio: ['ignore', 'pipe', 'pipe'],
186
+ timeout: 60_000,
187
+ })
188
+ const generated = JSON.parse(output) as { billing?: Record<string, string> }
189
+ expect(generated.billing).toEqual(E.billing)
190
+ }, 90_000)
191
+ })
192
+
193
+ describe('entity id → ORM table (resolveRegisteredEntityTableName)', () => {
194
+ it.each(ALL_IDS)('resolves %s to its table', (id) => {
195
+ expect(resolveRegisteredEntityTableName(em, id)).toBe(EXPECTED_TABLES[id])
196
+ })
197
+
198
+ it.each(ALL_IDS)('%s matches its entity class name', (id) => {
199
+ // The class-name lookup is the only path that works on Open Mercato 0.9
200
+ // (see the canary below). `billing_run` was `BillRun` until 0.20.0.
201
+ expect(classNameOf(id) in billingEntities).toBe(true)
202
+ // Look up by class-name string, as the framework does (MikroORM 7 types
203
+ // only admit class references here, but the runtime accepts names).
204
+ expect(orm.getMetadata().find(classNameOf(id) as never)?.tableName).toBe(EXPECTED_TABLES[id])
205
+ })
206
+
207
+ it('canary: the framework table-name fallback never matches on MikroORM 7', () => {
208
+ // `billing:runs` → candidate table `billing_runs`, which exists. The
209
+ // fallback iterates `MetadataStorage.getAll()` as an array, but MikroORM 7
210
+ // returns a Map, so it finds nothing. If this starts failing, upstream
211
+ // fixed it — the class-name rule in scripts/generate-entity-registry.mjs
212
+ // can then be relaxed.
213
+ expect(resolveRegisteredEntityTableName(em, 'billing:runs')).toBeNull()
214
+ })
215
+ })
216
+
217
+ describe('query index accepts billing writes', () => {
218
+ it('rejects billing ids while the app registry has `billing: {}` (the bug)', () => {
219
+ registerEntityIds(REGISTRY_WITHOUT_BILLING as never)
220
+ expect(() => resolveQueryIndexSourceMetadata(em, billingEntityIds.account)).toThrow(QueryIndexScopeError)
221
+ expect(() => resolveQueryIndexSourceMetadata(em, billingEntityIds.account)).toThrow(
222
+ 'Query index entity type is not registered: billing:billing_account',
223
+ )
224
+ })
225
+
226
+ it.each(ALL_IDS)('resolves the tenant-scoped source of %s once the shipped registry is registered', (id) => {
227
+ registerEntityIds({ ...REGISTRY_WITHOUT_BILLING, ...E } as never)
228
+ expect(resolveQueryIndexSourceMetadata(em, id)).toEqual({
229
+ table: EXPECTED_TABLES[id],
230
+ organizationColumn: 'organization_id',
231
+ tenantColumn: 'tenant_id',
232
+ })
233
+ })
234
+ })
235
+
236
+ describe('query engine reads billing ids from their tables', () => {
237
+ // The hybrid query engine checks `isOrmBackedSystemEntityId` before it
238
+ // consults `custom_entities`. Only a registered id short-circuits there, so
239
+ // an active `custom_entities` row (written by `mercato entities install`)
240
+ // can no longer route billing reads to custom-entity storage.
241
+ it('does not treat billing ids as ORM-backed while the registry has `billing: {}`', () => {
242
+ registerEntityIds(REGISTRY_WITHOUT_BILLING as never)
243
+ for (const id of ALL_IDS) {
244
+ expect({ id, ormBacked: isOrmBackedSystemEntityId(em, id) }).toEqual({ id, ormBacked: false })
245
+ }
246
+ })
247
+
248
+ it('treats every billing id as ORM-backed with the shipped registry', () => {
249
+ registerEntityIds({ ...REGISTRY_WITHOUT_BILLING, ...E } as never)
250
+ for (const id of ALL_IDS) {
251
+ expect({ id, ormBacked: isOrmBackedSystemEntityId(em, id) }).toEqual({ id, ormBacked: true })
252
+ }
253
+ })
254
+ })
255
+
256
+ describe('billing.accounts.create side effects', () => {
257
+ const TENANT = '11111111-1111-4111-8111-111111111111'
258
+ const ORG = '22222222-2222-4222-8222-222222222222'
259
+ const ACCOUNT_ID = '33333333-3333-4333-8333-333333333333'
260
+
261
+ it('emits a declared CRUD event and a query-index upsert the index accepts', async () => {
262
+ registerEntityIds({ ...REGISTRY_WITHOUT_BILLING, ...E } as never)
263
+
264
+ const emitted: Array<{ event: string; payload: Record<string, unknown> }> = []
265
+ const eventBus = {
266
+ emitEvent: jest.fn(async (event: string, payload: Record<string, unknown>) => {
267
+ emitted.push({ event, payload })
268
+ }),
269
+ }
270
+ const commandEm = {
271
+ fork: () => commandEm,
272
+ create: (_entity: unknown, data: Record<string, unknown>) => ({ id: ACCOUNT_ID, ...data }),
273
+ persist: jest.fn(),
274
+ flush: jest.fn(async () => undefined),
275
+ }
276
+ const container = {
277
+ resolve: (name: string) => {
278
+ if (name === 'eventBus') return eventBus
279
+ if (name === 'em') return commandEm
280
+ if (name === 'dataEngine') return dataEngine
281
+ throw new Error(`unexpected resolve(${name})`)
282
+ },
283
+ }
284
+ // The real data engine: it turns the command's side effects into the
285
+ // CRUD event and the `query_index.upsert_one` call.
286
+ const dataEngine = new DefaultDataEngine(em as never, container as never)
287
+
288
+ await createAccountCommand.execute(
289
+ {
290
+ tenantId: TENANT,
291
+ organizationId: ORG,
292
+ customerId: 'cust-1',
293
+ name: 'Acme',
294
+ currencyCode: 'EUR',
295
+ billCycle: 'monthly',
296
+ billCycleAnchor: '1',
297
+ invoiceEmail: 'billing@acme.test',
298
+ invoiceLanguage: 'en',
299
+ invoiceAddress: { line1: 'Main St 1', city: 'Berlin', postal_code: '10115', country: 'DE' },
300
+ nextBillDate: '2026-12-01',
301
+ } as never,
302
+ { auth: null, container } as never,
303
+ )
304
+ // The command bus flushes queued side effects after `execute`.
305
+ await dataEngine.flushOrmEntityChanges()
306
+
307
+ const crudEvent = emitted.find((entry) => entry.event.startsWith('billing.'))
308
+ expect(crudEvent?.event).toBe('billing.account.created')
309
+ expect(isEventDeclared('billing.account.created')).toBe(true)
310
+
311
+ const upsert = emitted.find((entry) => entry.event === 'query_index.upsert_one')
312
+ expect(upsert?.payload).toMatchObject({
313
+ entityType: 'billing:billing_account',
314
+ recordId: ACCOUNT_ID,
315
+ tenantId: TENANT,
316
+ organizationId: ORG,
317
+ })
318
+ // The first thing the `query_index.upsert_one` subscriber does — the
319
+ // call that threw QueryIndexScopeError and wrote `indexer_error_logs`.
320
+ expect(resolveQueryIndexSourceMetadata(em, String(upsert!.payload.entityType))).toEqual({
321
+ table: 'billing_accounts',
322
+ organizationColumn: 'organization_id',
323
+ tenantColumn: 'tenant_id',
324
+ })
325
+ })
326
+ })
@@ -0,0 +1,142 @@
1
+ /**
2
+ * CRUD event declarations.
3
+ *
4
+ * Commands emit CRUD events indirectly: `emitCrudSideEffects({ action,
5
+ * events: { module: 'billing', entity } })` makes the data engine emit
6
+ * `billing.<entity>.<action>`. An id missing from events.ts is still emitted,
7
+ * but logs "Emitting undeclared event" and stays invisible to the event
8
+ * registry (workflow triggers, subscriber discovery).
9
+ *
10
+ * The scan below reads every `emitCrudSideEffects` / `emitCrudUndoSideEffects`
11
+ * call in commands/ and requires the declared `crud` events to match exactly:
12
+ * nothing emitted is undeclared, nothing declared is never emitted.
13
+ */
14
+
15
+ import fs from 'node:fs'
16
+ import path from 'node:path'
17
+ import ts from 'typescript'
18
+
19
+ import { isEventDeclared } from '@open-mercato/shared/modules/events'
20
+
21
+ import eventsConfig from '../events'
22
+
23
+ const COMMANDS_DIR = path.resolve(__dirname, '../commands')
24
+ const SIDE_EFFECT_HELPERS = new Set(['emitCrudSideEffects', 'emitCrudUndoSideEffects'])
25
+
26
+ type Emission = { eventId: string; file: string }
27
+
28
+ function unwrap(expression: ts.Expression): ts.Expression {
29
+ if (ts.isAsExpression(expression) || ts.isParenthesizedExpression(expression) || ts.isSatisfiesExpression(expression)) {
30
+ return unwrap(expression.expression)
31
+ }
32
+ return expression
33
+ }
34
+
35
+ function propertyOf(objectLiteral: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined {
36
+ for (const property of objectLiteral.properties) {
37
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.text === name) {
38
+ return unwrap(property.initializer)
39
+ }
40
+ if (ts.isShorthandPropertyAssignment(property) && property.name.text === name) {
41
+ return property.name
42
+ }
43
+ }
44
+ return undefined
45
+ }
46
+
47
+ function stringValue(expression: ts.Expression | undefined): string | undefined {
48
+ return expression && ts.isStringLiteralLike(expression) ? expression.text : undefined
49
+ }
50
+
51
+ /** Every `billing.<entity>.<action>` a command file emits through the CRUD helpers. */
52
+ function scanCommandFile(file: string): Emission[] {
53
+ const sourceFile = ts.createSourceFile(file, fs.readFileSync(file, 'utf8'), ts.ScriptTarget.ES2022, true, ts.ScriptKind.TS)
54
+
55
+ // Module-level `const xEvents = { module, entity, … } as const`.
56
+ const eventConfigs = new Map<string, ts.ObjectLiteralExpression>()
57
+ for (const statement of sourceFile.statements) {
58
+ if (!ts.isVariableStatement(statement)) continue
59
+ for (const declaration of statement.declarationList.declarations) {
60
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue
61
+ const initializer = unwrap(declaration.initializer)
62
+ if (ts.isObjectLiteralExpression(initializer)) eventConfigs.set(declaration.name.text, initializer)
63
+ }
64
+ }
65
+
66
+ const emissions: Emission[] = []
67
+ const relativeFile = path.relative(COMMANDS_DIR, file)
68
+ const visit = (node: ts.Node) => {
69
+ if (
70
+ ts.isCallExpression(node) &&
71
+ ts.isIdentifier(node.expression) &&
72
+ SIDE_EFFECT_HELPERS.has(node.expression.text) &&
73
+ node.arguments[0] &&
74
+ ts.isObjectLiteralExpression(unwrap(node.arguments[0]))
75
+ ) {
76
+ const options = unwrap(node.arguments[0]) as ts.ObjectLiteralExpression
77
+ const eventsExpression = propertyOf(options, 'events')
78
+ if (eventsExpression) {
79
+ const config = ts.isIdentifier(eventsExpression)
80
+ ? eventConfigs.get(eventsExpression.text)
81
+ : ts.isObjectLiteralExpression(eventsExpression)
82
+ ? eventsExpression
83
+ : undefined
84
+ const moduleId = config ? stringValue(propertyOf(config, 'module')) : undefined
85
+ const entity = config ? stringValue(propertyOf(config, 'entity')) : undefined
86
+ const action = stringValue(propertyOf(options, 'action'))
87
+ if (!moduleId || !entity || !action) {
88
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart())
89
+ throw new Error(
90
+ `${relativeFile}:${line + 1}: cannot statically read the CRUD event of this ${node.expression.text} call — ` +
91
+ 'use a literal `action` and a module-level `events` config so this test can check it',
92
+ )
93
+ }
94
+ emissions.push({ eventId: `${moduleId}.${entity}.${action}`, file: relativeFile })
95
+ }
96
+ }
97
+ ts.forEachChild(node, visit)
98
+ }
99
+ visit(sourceFile)
100
+ return emissions
101
+ }
102
+
103
+ const emissions = fs
104
+ .readdirSync(COMMANDS_DIR)
105
+ .filter((name) => name.endsWith('.ts'))
106
+ .flatMap((name) => scanCommandFile(path.join(COMMANDS_DIR, name)))
107
+ const emittedIds = Array.from(new Set(emissions.map((emission) => emission.eventId))).sort()
108
+ const declaredCrudIds = eventsConfig.events
109
+ .filter((event) => event.category === 'crud')
110
+ .map((event) => event.id)
111
+ .sort()
112
+
113
+ describe('billing CRUD events', () => {
114
+ it('finds the CRUD emissions in commands/', () => {
115
+ // Guards the scanner itself: if it silently found nothing, the checks
116
+ // below would pass vacuously.
117
+ expect(emittedIds).toEqual(
118
+ expect.arrayContaining(['billing.account.created', 'billing.item.updated', 'billing.topup.created']),
119
+ )
120
+ })
121
+
122
+ it.each(emittedIds)('declares %s, which a command emits', (eventId) => {
123
+ expect(declaredCrudIds).toContain(eventId)
124
+ expect(isEventDeclared(eventId)).toBe(true)
125
+ })
126
+
127
+ it('declares no CRUD event that no command emits', () => {
128
+ expect(declaredCrudIds).toEqual(emittedIds)
129
+ })
130
+
131
+ it('declares every CRUD event against the entity in its id', () => {
132
+ for (const event of eventsConfig.events.filter((candidate) => candidate.category === 'crud')) {
133
+ expect({ id: event.id, entity: event.entity }).toEqual({ id: event.id, entity: event.id.split('.')[1] })
134
+ }
135
+ })
136
+
137
+ it('keeps the per-upload usage event out of workflow triggers, like billing.usage.rated', () => {
138
+ const byId = new Map(eventsConfig.events.map((event) => [event.id, event]))
139
+ expect(byId.get('billing.usage.created')?.excludeFromTriggers).toBe(true)
140
+ expect(byId.get('billing.usage.rated')?.excludeFromTriggers).toBe(true)
141
+ })
142
+ })