@dainamite/billing 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (598) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/modules/billing/acl.js +13 -1
  3. package/dist/modules/billing/acl.js.map +2 -2
  4. package/dist/modules/billing/api/accounts/[id]/summary/route.js +52 -0
  5. package/dist/modules/billing/api/accounts/[id]/summary/route.js.map +7 -0
  6. package/dist/modules/billing/api/accounts/route.js +46 -4
  7. package/dist/modules/billing/api/accounts/route.js.map +2 -2
  8. package/dist/modules/billing/api/export/account/[id]/route.js +11 -7
  9. package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
  10. package/dist/modules/billing/api/invoices/[id]/memos/route.js +54 -0
  11. package/dist/modules/billing/api/invoices/[id]/memos/route.js.map +7 -0
  12. package/dist/modules/billing/api/invoices/[id]/pdf/route.js +69 -0
  13. package/dist/modules/billing/api/invoices/[id]/pdf/route.js.map +7 -0
  14. package/dist/modules/billing/api/invoices/[id]/route.js +44 -24
  15. package/dist/modules/billing/api/invoices/[id]/route.js.map +2 -2
  16. package/dist/modules/billing/api/invoices/[id]/send/route.js +107 -0
  17. package/dist/modules/billing/api/invoices/[id]/send/route.js.map +7 -0
  18. package/dist/modules/billing/api/invoices/add-line/route.js +2 -2
  19. package/dist/modules/billing/api/invoices/add-line/route.js.map +2 -2
  20. package/dist/modules/billing/api/invoices/delete-draft/route.js +33 -0
  21. package/dist/modules/billing/api/invoices/delete-draft/route.js.map +7 -0
  22. package/dist/modules/billing/api/invoices/edit-line/route.js +2 -2
  23. package/dist/modules/billing/api/invoices/edit-line/route.js.map +2 -2
  24. package/dist/modules/billing/api/invoices/post/route.js +2 -2
  25. package/dist/modules/billing/api/invoices/post/route.js.map +2 -2
  26. package/dist/modules/billing/api/invoices/remove-line/route.js +2 -2
  27. package/dist/modules/billing/api/invoices/remove-line/route.js.map +2 -2
  28. package/dist/modules/billing/api/invoices/route.js +45 -46
  29. package/dist/modules/billing/api/invoices/route.js.map +2 -2
  30. package/dist/modules/billing/api/items/bulk/route.js +1 -1
  31. package/dist/modules/billing/api/items/bulk/route.js.map +2 -2
  32. package/dist/modules/billing/api/items/change-price/route.js +44 -0
  33. package/dist/modules/billing/api/items/change-price/route.js.map +7 -0
  34. package/dist/modules/billing/api/items/route.js +19 -7
  35. package/dist/modules/billing/api/items/route.js.map +2 -2
  36. package/dist/modules/billing/api/payment-imports/[id]/route.js +49 -0
  37. package/dist/modules/billing/api/payment-imports/[id]/route.js.map +7 -0
  38. package/dist/modules/billing/api/payment-imports/route.js +57 -0
  39. package/dist/modules/billing/api/payment-imports/route.js.map +7 -0
  40. package/dist/modules/billing/api/payments/[id]/route.js +60 -0
  41. package/dist/modules/billing/api/payments/[id]/route.js.map +7 -0
  42. package/dist/modules/billing/api/payments/allocate/route.js +35 -0
  43. package/dist/modules/billing/api/payments/allocate/route.js.map +7 -0
  44. package/dist/modules/billing/api/payments/assign/route.js +36 -0
  45. package/dist/modules/billing/api/payments/assign/route.js.map +7 -0
  46. package/dist/modules/billing/api/payments/convert-to-topup/route.js +35 -0
  47. package/dist/modules/billing/api/payments/convert-to-topup/route.js.map +7 -0
  48. package/dist/modules/billing/api/payments/ignore/route.js +35 -0
  49. package/dist/modules/billing/api/payments/ignore/route.js.map +7 -0
  50. package/dist/modules/billing/api/payments/import/route.js +63 -0
  51. package/dist/modules/billing/api/payments/import/route.js.map +7 -0
  52. package/dist/modules/billing/api/payments/restore/route.js +35 -0
  53. package/dist/modules/billing/api/payments/restore/route.js.map +7 -0
  54. package/dist/modules/billing/api/payments/route.js +114 -0
  55. package/dist/modules/billing/api/payments/route.js.map +7 -0
  56. package/dist/modules/billing/api/payments/unallocate/route.js +35 -0
  57. package/dist/modules/billing/api/payments/unallocate/route.js.map +7 -0
  58. package/dist/modules/billing/api/payments/unassign/route.js +35 -0
  59. package/dist/modules/billing/api/payments/unassign/route.js.map +7 -0
  60. package/dist/modules/billing/api/run-outcomes/route.js +43 -6
  61. package/dist/modules/billing/api/run-outcomes/route.js.map +2 -2
  62. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js +61 -0
  63. package/dist/modules/billing/api/runs/[id]/post-drafts/route.js.map +7 -0
  64. package/dist/modules/billing/api/runs/[id]/route.js +53 -0
  65. package/dist/modules/billing/api/runs/[id]/route.js.map +7 -0
  66. package/dist/modules/billing/api/runs/retry-failed/route.js +1 -1
  67. package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
  68. package/dist/modules/billing/api/runs/route.js +9 -3
  69. package/dist/modules/billing/api/runs/route.js.map +2 -2
  70. package/dist/modules/billing/api/settings/number-preview/route.js +42 -0
  71. package/dist/modules/billing/api/settings/number-preview/route.js.map +7 -0
  72. package/dist/modules/billing/api/settings/route.js +92 -0
  73. package/dist/modules/billing/api/settings/route.js.map +7 -0
  74. package/dist/modules/billing/api/statements/route.js +3 -0
  75. package/dist/modules/billing/api/statements/route.js.map +2 -2
  76. package/dist/modules/billing/api/test-invoices/wipe/route.js +2 -2
  77. package/dist/modules/billing/api/test-invoices/wipe/route.js.map +2 -2
  78. package/dist/modules/billing/backend/billing/accounts/[id]/page.js +121 -232
  79. package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
  80. package/dist/modules/billing/backend/billing/accounts/create/page.js +6 -0
  81. package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
  82. package/dist/modules/billing/backend/billing/accounts/page.js +63 -4
  83. package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
  84. package/dist/modules/billing/backend/billing/invoices/[id]/page.js +297 -65
  85. package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +3 -3
  86. package/dist/modules/billing/backend/billing/invoices/page.js +6 -172
  87. package/dist/modules/billing/backend/billing/invoices/page.js.map +2 -2
  88. package/dist/modules/billing/backend/billing/items/[id]/page.js +51 -7
  89. package/dist/modules/billing/backend/billing/items/[id]/page.js.map +2 -2
  90. package/dist/modules/billing/backend/billing/items/create/page.js +9 -6
  91. package/dist/modules/billing/backend/billing/items/create/page.js.map +2 -2
  92. package/dist/modules/billing/backend/billing/items/page.js +8 -2
  93. package/dist/modules/billing/backend/billing/items/page.js.map +2 -2
  94. package/dist/modules/billing/backend/billing/payments/[id]/page.js +297 -0
  95. package/dist/modules/billing/backend/billing/payments/[id]/page.js.map +7 -0
  96. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js +21 -0
  97. package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js.map +7 -0
  98. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js +74 -0
  99. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js.map +7 -0
  100. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js +21 -0
  101. package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js.map +7 -0
  102. package/dist/modules/billing/backend/billing/payments/page.js +58 -0
  103. package/dist/modules/billing/backend/billing/payments/page.js.map +7 -0
  104. package/dist/modules/billing/backend/billing/payments/page.meta.js +18 -0
  105. package/dist/modules/billing/backend/billing/payments/page.meta.js.map +7 -0
  106. package/dist/modules/billing/backend/billing/runs/[id]/page.js +49 -14
  107. package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +2 -2
  108. package/dist/modules/billing/backend/billing/runs/page.js +58 -7
  109. package/dist/modules/billing/backend/billing/runs/page.js.map +2 -2
  110. package/dist/modules/billing/backend/billing/statements/[id]/page.js +61 -6
  111. package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +2 -2
  112. package/dist/modules/billing/backend/billing/statements/page.js +1 -1
  113. package/dist/modules/billing/backend/billing/statements/page.js.map +2 -2
  114. package/dist/modules/billing/backend/billing/topups/page.js +1 -1
  115. package/dist/modules/billing/backend/billing/topups/page.js.map +2 -2
  116. package/dist/modules/billing/backend/billing/transactions/page.js +1 -1
  117. package/dist/modules/billing/backend/billing/transactions/page.js.map +2 -2
  118. package/dist/modules/billing/backend/billing/usage/create/page.js +3 -2
  119. package/dist/modules/billing/backend/billing/usage/create/page.js.map +2 -2
  120. package/dist/modules/billing/backend/config/billing/page.js +472 -0
  121. package/dist/modules/billing/backend/config/billing/page.js.map +7 -0
  122. package/dist/modules/billing/backend/config/billing/page.meta.js +19 -0
  123. package/dist/modules/billing/backend/config/billing/page.meta.js.map +7 -0
  124. package/dist/modules/billing/cli.js +22 -1
  125. package/dist/modules/billing/cli.js.map +2 -2
  126. package/dist/modules/billing/commands/accounts.js +6 -0
  127. package/dist/modules/billing/commands/accounts.js.map +2 -2
  128. package/dist/modules/billing/commands/index.js +3 -0
  129. package/dist/modules/billing/commands/index.js.map +2 -2
  130. package/dist/modules/billing/commands/invoices.js +269 -167
  131. package/dist/modules/billing/commands/invoices.js.map +2 -2
  132. package/dist/modules/billing/commands/items.js +108 -0
  133. package/dist/modules/billing/commands/items.js.map +2 -2
  134. package/dist/modules/billing/commands/memos.js +179 -0
  135. package/dist/modules/billing/commands/memos.js.map +7 -0
  136. package/dist/modules/billing/commands/payments.js +379 -0
  137. package/dist/modules/billing/commands/payments.js.map +7 -0
  138. package/dist/modules/billing/commands/runs.js +151 -4
  139. package/dist/modules/billing/commands/runs.js.map +2 -2
  140. package/dist/modules/billing/commands/settings.js +59 -0
  141. package/dist/modules/billing/commands/settings.js.map +7 -0
  142. package/dist/modules/billing/commands/topups.js +9 -2
  143. package/dist/modules/billing/commands/topups.js.map +2 -2
  144. package/dist/modules/billing/commands/transactions.js +2 -2
  145. package/dist/modules/billing/commands/transactions.js.map +2 -2
  146. package/dist/modules/billing/components/AccountForm.js +149 -42
  147. package/dist/modules/billing/components/AccountForm.js.map +2 -2
  148. package/dist/modules/billing/components/AccountInvoicesSection.js +64 -0
  149. package/dist/modules/billing/components/AccountInvoicesSection.js.map +7 -0
  150. package/dist/modules/billing/components/AccountPaymentsSection.js +43 -0
  151. package/dist/modules/billing/components/AccountPaymentsSection.js.map +7 -0
  152. package/dist/modules/billing/components/AccountPicker.js +56 -0
  153. package/dist/modules/billing/components/AccountPicker.js.map +7 -0
  154. package/dist/modules/billing/components/AllocatePaymentDialog.js +134 -0
  155. package/dist/modules/billing/components/AllocatePaymentDialog.js.map +7 -0
  156. package/dist/modules/billing/components/BillingDataTable.js +21 -0
  157. package/dist/modules/billing/components/BillingDataTable.js.map +7 -0
  158. package/dist/modules/billing/components/BillingSettingsLink.js +19 -0
  159. package/dist/modules/billing/components/BillingSettingsLink.js.map +7 -0
  160. package/dist/modules/billing/components/ChangeItemPriceDialog.js +158 -0
  161. package/dist/modules/billing/components/ChangeItemPriceDialog.js.map +7 -0
  162. package/dist/modules/billing/components/CreateMemoDialog.js +200 -0
  163. package/dist/modules/billing/components/CreateMemoDialog.js.map +7 -0
  164. package/dist/modules/billing/components/CustomerLabel.js +33 -0
  165. package/dist/modules/billing/components/CustomerLabel.js.map +7 -0
  166. package/dist/modules/billing/components/InvoicesTable.js +270 -0
  167. package/dist/modules/billing/components/InvoicesTable.js.map +7 -0
  168. package/dist/modules/billing/components/ItemForm.js +176 -44
  169. package/dist/modules/billing/components/ItemForm.js.map +2 -2
  170. package/dist/modules/billing/components/ItemPriceField.js +46 -0
  171. package/dist/modules/billing/components/ItemPriceField.js.map +7 -0
  172. package/dist/modules/billing/components/LineFormDialog.js +25 -2
  173. package/dist/modules/billing/components/LineFormDialog.js.map +2 -2
  174. package/dist/modules/billing/components/ManualPaymentDialog.js +122 -0
  175. package/dist/modules/billing/components/ManualPaymentDialog.js.map +7 -0
  176. package/dist/modules/billing/components/MoneyInput.js +14 -0
  177. package/dist/modules/billing/components/MoneyInput.js.map +7 -0
  178. package/dist/modules/billing/components/PaymentUploadDialog.js +115 -0
  179. package/dist/modules/billing/components/PaymentUploadDialog.js.map +7 -0
  180. package/dist/modules/billing/components/PaymentsTable.js +171 -0
  181. package/dist/modules/billing/components/PaymentsTable.js.map +7 -0
  182. package/dist/modules/billing/components/PostRunDraftsButton.js +105 -0
  183. package/dist/modules/billing/components/PostRunDraftsButton.js.map +7 -0
  184. package/dist/modules/billing/components/PrepaidPanel.js +14 -4
  185. package/dist/modules/billing/components/PrepaidPanel.js.map +2 -2
  186. package/dist/modules/billing/components/RunCyclesSection.js +209 -0
  187. package/dist/modules/billing/components/RunCyclesSection.js.map +7 -0
  188. package/dist/modules/billing/components/TestRunActions.js +127 -0
  189. package/dist/modules/billing/components/TestRunActions.js.map +7 -0
  190. package/dist/modules/billing/components/account/AccountItemsTab.js +157 -0
  191. package/dist/modules/billing/components/account/AccountItemsTab.js.map +7 -0
  192. package/dist/modules/billing/components/account/AccountRunBillingDialog.js +122 -0
  193. package/dist/modules/billing/components/account/AccountRunBillingDialog.js.map +7 -0
  194. package/dist/modules/billing/components/account/AccountRunsTab.js +152 -0
  195. package/dist/modules/billing/components/account/AccountRunsTab.js.map +7 -0
  196. package/dist/modules/billing/components/account/AccountStatementsTab.js +98 -0
  197. package/dist/modules/billing/components/account/AccountStatementsTab.js.map +7 -0
  198. package/dist/modules/billing/components/account/AccountSummaryCards.js +125 -0
  199. package/dist/modules/billing/components/account/AccountSummaryCards.js.map +7 -0
  200. package/dist/modules/billing/components/account/AccountUsageTab.js +91 -0
  201. package/dist/modules/billing/components/account/AccountUsageTab.js.map +7 -0
  202. package/dist/modules/billing/components/account/AccountView.js +73 -0
  203. package/dist/modules/billing/components/account/AccountView.js.map +7 -0
  204. package/dist/modules/billing/components/account/accountRow.js +39 -0
  205. package/dist/modules/billing/components/account/accountRow.js.map +7 -0
  206. package/dist/modules/billing/components/customerNames.js +42 -0
  207. package/dist/modules/billing/components/customerNames.js.map +7 -0
  208. package/dist/modules/billing/data/entities.js +560 -4
  209. package/dist/modules/billing/data/entities.js.map +2 -2
  210. package/dist/modules/billing/data/entityIds.js +10 -1
  211. package/dist/modules/billing/data/entityIds.js.map +2 -2
  212. package/dist/modules/billing/data/validators.js +269 -15
  213. package/dist/modules/billing/data/validators.js.map +2 -2
  214. package/dist/modules/billing/events.js +50 -0
  215. package/dist/modules/billing/events.js.map +2 -2
  216. package/dist/modules/billing/i18n/en.json +457 -21
  217. package/dist/modules/billing/i18n/pl.json +457 -21
  218. package/dist/modules/billing/index.js +6 -2
  219. package/dist/modules/billing/index.js.map +2 -2
  220. package/dist/modules/billing/lib/accountFinancials.js +58 -0
  221. package/dist/modules/billing/lib/accountFinancials.js.map +7 -0
  222. package/dist/modules/billing/lib/accountLock.js +14 -0
  223. package/dist/modules/billing/lib/accountLock.js.map +7 -0
  224. package/dist/modules/billing/lib/accountStatus.js +9 -7
  225. package/dist/modules/billing/lib/accountStatus.js.map +2 -2
  226. package/dist/modules/billing/lib/accountSummary.js +106 -0
  227. package/dist/modules/billing/lib/accountSummary.js.map +7 -0
  228. package/dist/modules/billing/lib/allocation.js +356 -0
  229. package/dist/modules/billing/lib/allocation.js.map +7 -0
  230. package/dist/modules/billing/lib/allocationEvents.js +22 -0
  231. package/dist/modules/billing/lib/allocationEvents.js.map +7 -0
  232. package/dist/modules/billing/lib/billPeriod.js +15 -1
  233. package/dist/modules/billing/lib/billPeriod.js.map +2 -2
  234. package/dist/modules/billing/lib/billRunEngine.js +237 -102
  235. package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
  236. package/dist/modules/billing/lib/billingSettings.js +116 -0
  237. package/dist/modules/billing/lib/billingSettings.js.map +7 -0
  238. package/dist/modules/billing/lib/commandRoute.js +81 -0
  239. package/dist/modules/billing/lib/commandRoute.js.map +7 -0
  240. package/dist/modules/billing/lib/customerLookup.js +97 -0
  241. package/dist/modules/billing/lib/customerLookup.js.map +7 -0
  242. package/dist/modules/billing/lib/dateOnly.js +13 -0
  243. package/dist/modules/billing/lib/dateOnly.js.map +7 -0
  244. package/dist/modules/billing/lib/documentParties.js +16 -0
  245. package/dist/modules/billing/lib/documentParties.js.map +7 -0
  246. package/dist/modules/billing/lib/documentTypes.js +27 -0
  247. package/dist/modules/billing/lib/documentTypes.js.map +7 -0
  248. package/dist/modules/billing/lib/dueDate.js +18 -0
  249. package/dist/modules/billing/lib/dueDate.js.map +7 -0
  250. package/dist/modules/billing/lib/iban.js +29 -0
  251. package/dist/modules/billing/lib/iban.js.map +7 -0
  252. package/dist/modules/billing/lib/invoiceDelivery.js +131 -0
  253. package/dist/modules/billing/lib/invoiceDelivery.js.map +7 -0
  254. package/dist/modules/billing/lib/invoiceLanguages.js +16 -0
  255. package/dist/modules/billing/lib/invoiceLanguages.js.map +7 -0
  256. package/dist/modules/billing/lib/invoiceWriter.js +107 -99
  257. package/dist/modules/billing/lib/invoiceWriter.js.map +2 -2
  258. package/dist/modules/billing/lib/itemBillingState.js +27 -0
  259. package/dist/modules/billing/lib/itemBillingState.js.map +7 -0
  260. package/dist/modules/billing/lib/itemPriceChange.js +18 -0
  261. package/dist/modules/billing/lib/itemPriceChange.js.map +7 -0
  262. package/dist/modules/billing/lib/itemSelector.js.map +2 -2
  263. package/dist/modules/billing/lib/lineAmounts.js +32 -0
  264. package/dist/modules/billing/lib/lineAmounts.js.map +7 -0
  265. package/dist/modules/billing/lib/memoGuards.js +25 -0
  266. package/dist/modules/billing/lib/memoGuards.js.map +7 -0
  267. package/dist/modules/billing/lib/navigation.js +48 -0
  268. package/dist/modules/billing/lib/navigation.js.map +7 -0
  269. package/dist/modules/billing/lib/numbering.js +120 -0
  270. package/dist/modules/billing/lib/numbering.js.map +7 -0
  271. package/dist/modules/billing/lib/overdue.js +36 -0
  272. package/dist/modules/billing/lib/overdue.js.map +7 -0
  273. package/dist/modules/billing/lib/paymentCsv.js +125 -0
  274. package/dist/modules/billing/lib/paymentCsv.js.map +7 -0
  275. package/dist/modules/billing/lib/paymentGateway.js +15 -0
  276. package/dist/modules/billing/lib/paymentGateway.js.map +7 -0
  277. package/dist/modules/billing/lib/paymentImport.js +164 -0
  278. package/dist/modules/billing/lib/paymentImport.js.map +7 -0
  279. package/dist/modules/billing/lib/paymentMatcher.js +36 -0
  280. package/dist/modules/billing/lib/paymentMatcher.js.map +7 -0
  281. package/dist/modules/billing/lib/pdf/attachmentStore.js +46 -0
  282. package/dist/modules/billing/lib/pdf/attachmentStore.js.map +7 -0
  283. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js +7 -0
  284. package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js.map +7 -0
  285. package/dist/modules/billing/lib/pdf/invoiceModel.js +194 -0
  286. package/dist/modules/billing/lib/pdf/invoiceModel.js.map +7 -0
  287. package/dist/modules/billing/lib/pdf/invoicePdf.js +165 -0
  288. package/dist/modules/billing/lib/pdf/invoicePdf.js.map +7 -0
  289. package/dist/modules/billing/lib/pdf/invoiceTemplate.js +274 -0
  290. package/dist/modules/billing/lib/pdf/invoiceTemplate.js.map +7 -0
  291. package/dist/modules/billing/lib/prepaidConfig.js +17 -25
  292. package/dist/modules/billing/lib/prepaidConfig.js.map +2 -2
  293. package/dist/modules/billing/lib/prepaidConsume.js +26 -7
  294. package/dist/modules/billing/lib/prepaidConsume.js.map +2 -2
  295. package/dist/modules/billing/lib/prepaidPeriodClose.js +277 -158
  296. package/dist/modules/billing/lib/prepaidPeriodClose.js.map +2 -2
  297. package/dist/modules/billing/lib/prepaidTaxPoint.js +37 -0
  298. package/dist/modules/billing/lib/prepaidTaxPoint.js.map +7 -0
  299. package/dist/modules/billing/lib/seeds/demo.js +51 -3
  300. package/dist/modules/billing/lib/seeds/demo.js.map +2 -2
  301. package/dist/modules/billing/lib/seeds/paymentsSample.js +93 -0
  302. package/dist/modules/billing/lib/seeds/paymentsSample.js.map +7 -0
  303. package/dist/modules/billing/lib/seeds/sellerSettings.js +41 -0
  304. package/dist/modules/billing/lib/seeds/sellerSettings.js.map +7 -0
  305. package/dist/modules/billing/lib/servicePeriods.js +121 -0
  306. package/dist/modules/billing/lib/servicePeriods.js.map +7 -0
  307. package/dist/modules/billing/lib/testRunCleanup.js +24 -0
  308. package/dist/modules/billing/lib/testRunCleanup.js.map +7 -0
  309. package/dist/modules/billing/lib/topupCapture.js +2 -2
  310. package/dist/modules/billing/lib/topupCapture.js.map +2 -2
  311. package/dist/modules/billing/lib/topupReceipt.js +58 -79
  312. package/dist/modules/billing/lib/topupReceipt.js.map +2 -2
  313. package/dist/modules/billing/lib/usageRater.js.map +1 -1
  314. package/dist/modules/billing/lib/usageRunner.js +14 -6
  315. package/dist/modules/billing/lib/usageRunner.js.map +2 -2
  316. package/dist/modules/billing/lib/vat.js +39 -0
  317. package/dist/modules/billing/lib/vat.js.map +7 -0
  318. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js +15 -0
  319. package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js.map +7 -0
  320. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js +25 -0
  321. package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js.map +7 -0
  322. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js +115 -0
  323. package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js.map +7 -0
  324. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js +47 -0
  325. package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js.map +7 -0
  326. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js +89 -0
  327. package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js.map +7 -0
  328. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js +17 -0
  329. package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js.map +7 -0
  330. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js +27 -0
  331. package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js.map +7 -0
  332. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js +29 -0
  333. package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js.map +7 -0
  334. package/dist/modules/billing/setup.js +7 -184
  335. package/dist/modules/billing/setup.js.map +2 -2
  336. package/dist/modules/billing/subscribers/credit-memo-auto-send.js +16 -0
  337. package/dist/modules/billing/subscribers/credit-memo-auto-send.js.map +7 -0
  338. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js +16 -0
  339. package/dist/modules/billing/subscribers/debit-memo-apply-credit.js.map +7 -0
  340. package/dist/modules/billing/subscribers/debit-memo-auto-send.js +16 -0
  341. package/dist/modules/billing/subscribers/debit-memo-auto-send.js.map +7 -0
  342. package/dist/modules/billing/subscribers/invoice-apply-credit.js +34 -0
  343. package/dist/modules/billing/subscribers/invoice-apply-credit.js.map +7 -0
  344. package/dist/modules/billing/subscribers/invoice-auto-send.js +30 -0
  345. package/dist/modules/billing/subscribers/invoice-auto-send.js.map +7 -0
  346. package/dist/modules/billing/subscribers/payment-captured-to-paid.js +69 -33
  347. package/dist/modules/billing/subscribers/payment-captured-to-paid.js.map +2 -2
  348. package/dist/modules/billing/subscribers/prepaid-topup-captured.js +4 -6
  349. package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +2 -2
  350. package/generated/entities/billing_account/index.ts +3 -0
  351. package/generated/entities/billing_allocation/index.ts +18 -0
  352. package/generated/entities/billing_document_sequence/index.ts +11 -0
  353. package/generated/entities/billing_invoice/index.ts +38 -0
  354. package/generated/entities/billing_invoice_line/index.ts +24 -0
  355. package/generated/entities/billing_item/index.ts +4 -0
  356. package/generated/entities/billing_payment/index.ts +25 -0
  357. package/generated/entities/billing_payment_import/index.ts +14 -0
  358. package/generated/entities/billing_run/index.ts +0 -1
  359. package/generated/entities/billing_run_outcome/index.ts +1 -0
  360. package/generated/entities/billing_settings/index.ts +26 -0
  361. package/generated/entities/billing_statement/index.ts +1 -0
  362. package/generated/entities.ids.generated.ts +7 -0
  363. package/package.json +5 -1
  364. package/src/modules/billing/__integration__/TC-BILL-004.spec.ts +170 -0
  365. package/src/modules/billing/__integration__/TC-BILL-005.spec.ts +134 -0
  366. package/src/modules/billing/__integration__/TC-BILL-006.spec.ts +101 -0
  367. package/src/modules/billing/__integration__/TC-BILL-007.spec.ts +140 -0
  368. package/src/modules/billing/__integration__/TC-BILL-008.spec.ts +108 -0
  369. package/src/modules/billing/__integration__/TC-BILL-009.spec.ts +113 -0
  370. package/src/modules/billing/__integration__/TC-BILL-010.spec.ts +118 -0
  371. package/src/modules/billing/__integration__/TC-BILL-011.spec.ts +142 -0
  372. package/src/modules/billing/__integration__/TC-BILL-012.spec.ts +115 -0
  373. package/src/modules/billing/__integration__/TC-BILL-013.spec.ts +118 -0
  374. package/src/modules/billing/__integration__/sellerSettings.ts +24 -0
  375. package/src/modules/billing/__tests__/commands/invoices.test.ts +436 -354
  376. package/src/modules/billing/__tests__/commands/itemsBulk.test.ts +33 -0
  377. package/src/modules/billing/__tests__/commands/memos.test.ts +205 -0
  378. package/src/modules/billing/__tests__/commands/payments.test.ts +241 -0
  379. package/src/modules/billing/__tests__/commands/runsDelete.test.ts +146 -0
  380. package/src/modules/billing/__tests__/commands/runsPostDrafts.test.ts +106 -0
  381. package/src/modules/billing/__tests__/commands/topups.test.ts +11 -0
  382. package/src/modules/billing/__tests__/entityRegistry.test.ts +8 -1
  383. package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +28 -11
  384. package/src/modules/billing/__tests__/lib/accountFinancials.test.ts +53 -0
  385. package/src/modules/billing/__tests__/lib/accountSummary.test.ts +97 -0
  386. package/src/modules/billing/__tests__/lib/allocation.test.ts +319 -0
  387. package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +228 -17
  388. package/src/modules/billing/__tests__/lib/billingSettings.test.ts +109 -0
  389. package/src/modules/billing/__tests__/lib/customerLookup.test.ts +156 -0
  390. package/src/modules/billing/__tests__/lib/dueDate.test.ts +35 -0
  391. package/src/modules/billing/__tests__/lib/iban.test.ts +38 -0
  392. package/src/modules/billing/__tests__/lib/invoiceDelivery.test.ts +189 -0
  393. package/src/modules/billing/__tests__/lib/invoiceWriter.test.ts +212 -10
  394. package/src/modules/billing/__tests__/lib/itemBillingState.test.ts +42 -0
  395. package/src/modules/billing/__tests__/lib/itemPriceChange.test.ts +165 -0
  396. package/src/modules/billing/__tests__/lib/lineAmounts.test.ts +86 -0
  397. package/src/modules/billing/__tests__/lib/navigation.test.ts +71 -0
  398. package/src/modules/billing/__tests__/lib/numbering.test.ts +91 -0
  399. package/src/modules/billing/__tests__/lib/overdue.test.ts +36 -0
  400. package/src/modules/billing/__tests__/lib/paymentCsv.test.ts +148 -0
  401. package/src/modules/billing/__tests__/lib/paymentImport.test.ts +113 -0
  402. package/src/modules/billing/__tests__/lib/paymentsSample.test.ts +73 -0
  403. package/src/modules/billing/__tests__/lib/pdf/invoicePdf.test.ts +199 -0
  404. package/src/modules/billing/__tests__/lib/pdf/invoicePdfService.test.ts +147 -0
  405. package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +374 -98
  406. package/src/modules/billing/__tests__/lib/prepaidTaxPoint.test.ts +118 -0
  407. package/src/modules/billing/__tests__/lib/seedsDemo.test.ts +53 -2
  408. package/src/modules/billing/__tests__/lib/sellerSettings.test.ts +28 -0
  409. package/src/modules/billing/__tests__/lib/servicePeriods.test.ts +190 -0
  410. package/src/modules/billing/__tests__/lib/topupCapture.test.ts +6 -28
  411. package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +21 -52
  412. package/src/modules/billing/__tests__/lib/vat.test.ts +51 -0
  413. package/src/modules/billing/__tests__/package-isolation.test.ts +22 -8
  414. package/src/modules/billing/__tests__/setup.test.ts +13 -4
  415. package/src/modules/billing/__tests__/validators.test.ts +193 -3
  416. package/src/modules/billing/acl.ts +12 -0
  417. package/src/modules/billing/api/accounts/[id]/summary/route.ts +62 -0
  418. package/src/modules/billing/api/accounts/route.ts +58 -4
  419. package/src/modules/billing/api/export/account/[id]/route.ts +13 -11
  420. package/src/modules/billing/api/invoices/[id]/memos/route.ts +63 -0
  421. package/src/modules/billing/api/invoices/[id]/pdf/route.ts +79 -0
  422. package/src/modules/billing/api/invoices/[id]/route.ts +57 -70
  423. package/src/modules/billing/api/invoices/[id]/send/route.ts +127 -0
  424. package/src/modules/billing/api/invoices/add-line/route.ts +2 -2
  425. package/src/modules/billing/api/invoices/delete-draft/route.ts +33 -0
  426. package/src/modules/billing/api/invoices/edit-line/route.ts +2 -2
  427. package/src/modules/billing/api/invoices/post/route.ts +7 -6
  428. package/src/modules/billing/api/invoices/remove-line/route.ts +2 -2
  429. package/src/modules/billing/api/invoices/route.ts +81 -86
  430. package/src/modules/billing/api/items/bulk/route.ts +2 -1
  431. package/src/modules/billing/api/items/change-price/route.ts +54 -0
  432. package/src/modules/billing/api/items/route.ts +23 -7
  433. package/src/modules/billing/api/payment-imports/[id]/route.ts +49 -0
  434. package/src/modules/billing/api/payment-imports/route.ts +57 -0
  435. package/src/modules/billing/api/payments/[id]/route.ts +63 -0
  436. package/src/modules/billing/api/payments/allocate/route.ts +35 -0
  437. package/src/modules/billing/api/payments/assign/route.ts +36 -0
  438. package/src/modules/billing/api/payments/convert-to-topup/route.ts +35 -0
  439. package/src/modules/billing/api/payments/ignore/route.ts +35 -0
  440. package/src/modules/billing/api/payments/import/route.ts +72 -0
  441. package/src/modules/billing/api/payments/restore/route.ts +35 -0
  442. package/src/modules/billing/api/payments/route.ts +147 -0
  443. package/src/modules/billing/api/payments/unallocate/route.ts +35 -0
  444. package/src/modules/billing/api/payments/unassign/route.ts +35 -0
  445. package/src/modules/billing/api/run-outcomes/route.ts +49 -6
  446. package/src/modules/billing/api/runs/[id]/post-drafts/route.ts +71 -0
  447. package/src/modules/billing/api/runs/[id]/route.ts +59 -0
  448. package/src/modules/billing/api/runs/retry-failed/route.ts +2 -1
  449. package/src/modules/billing/api/runs/route.ts +15 -7
  450. package/src/modules/billing/api/settings/number-preview/route.ts +46 -0
  451. package/src/modules/billing/api/settings/route.ts +116 -0
  452. package/src/modules/billing/api/statements/route.ts +5 -0
  453. package/src/modules/billing/api/test-invoices/wipe/route.ts +2 -2
  454. package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +151 -372
  455. package/src/modules/billing/backend/billing/accounts/create/page.tsx +6 -0
  456. package/src/modules/billing/backend/billing/accounts/page.tsx +70 -4
  457. package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +444 -95
  458. package/src/modules/billing/backend/billing/invoices/page.tsx +8 -227
  459. package/src/modules/billing/backend/billing/items/[id]/page.tsx +75 -6
  460. package/src/modules/billing/backend/billing/items/create/page.tsx +13 -8
  461. package/src/modules/billing/backend/billing/items/page.tsx +18 -2
  462. package/src/modules/billing/backend/billing/payments/[id]/page.meta.ts +19 -0
  463. package/src/modules/billing/backend/billing/payments/[id]/page.tsx +415 -0
  464. package/src/modules/billing/backend/billing/payments/imports/[id]/page.meta.ts +19 -0
  465. package/src/modules/billing/backend/billing/payments/imports/[id]/page.tsx +106 -0
  466. package/src/modules/billing/backend/billing/payments/page.meta.ts +16 -0
  467. package/src/modules/billing/backend/billing/payments/page.tsx +56 -0
  468. package/src/modules/billing/backend/billing/runs/[id]/page.tsx +49 -18
  469. package/src/modules/billing/backend/billing/runs/page.tsx +59 -8
  470. package/src/modules/billing/backend/billing/statements/[id]/page.tsx +86 -5
  471. package/src/modules/billing/backend/billing/statements/page.tsx +1 -1
  472. package/src/modules/billing/backend/billing/topups/page.tsx +1 -1
  473. package/src/modules/billing/backend/billing/transactions/page.tsx +1 -1
  474. package/src/modules/billing/backend/billing/usage/create/page.tsx +3 -2
  475. package/src/modules/billing/backend/config/billing/page.meta.ts +21 -0
  476. package/src/modules/billing/backend/config/billing/page.tsx +549 -0
  477. package/src/modules/billing/cli.ts +31 -1
  478. package/src/modules/billing/commands/accounts.ts +6 -0
  479. package/src/modules/billing/commands/index.ts +3 -0
  480. package/src/modules/billing/commands/invoices.ts +364 -231
  481. package/src/modules/billing/commands/items.ts +143 -0
  482. package/src/modules/billing/commands/memos.ts +218 -0
  483. package/src/modules/billing/commands/payments.ts +428 -0
  484. package/src/modules/billing/commands/runs.ts +205 -3
  485. package/src/modules/billing/commands/settings.ts +77 -0
  486. package/src/modules/billing/commands/topups.ts +12 -3
  487. package/src/modules/billing/commands/transactions.ts +2 -2
  488. package/src/modules/billing/components/AccountForm.tsx +199 -53
  489. package/src/modules/billing/components/AccountInvoicesSection.tsx +75 -0
  490. package/src/modules/billing/components/AccountPaymentsSection.tsx +42 -0
  491. package/src/modules/billing/components/AccountPicker.tsx +70 -0
  492. package/src/modules/billing/components/AllocatePaymentDialog.tsx +167 -0
  493. package/src/modules/billing/components/BillingDataTable.tsx +34 -0
  494. package/src/modules/billing/components/BillingSettingsLink.tsx +25 -0
  495. package/src/modules/billing/components/ChangeItemPriceDialog.tsx +180 -0
  496. package/src/modules/billing/components/CreateMemoDialog.tsx +232 -0
  497. package/src/modules/billing/components/CustomerLabel.tsx +53 -0
  498. package/src/modules/billing/components/InvoicesTable.tsx +344 -0
  499. package/src/modules/billing/components/ItemForm.tsx +233 -44
  500. package/src/modules/billing/components/ItemPriceField.tsx +81 -0
  501. package/src/modules/billing/components/LineFormDialog.tsx +25 -1
  502. package/src/modules/billing/components/ManualPaymentDialog.tsx +140 -0
  503. package/src/modules/billing/components/MoneyInput.tsx +26 -0
  504. package/src/modules/billing/components/PaymentUploadDialog.tsx +148 -0
  505. package/src/modules/billing/components/PaymentsTable.tsx +213 -0
  506. package/src/modules/billing/components/PostRunDraftsButton.tsx +129 -0
  507. package/src/modules/billing/components/PrepaidPanel.tsx +19 -6
  508. package/src/modules/billing/components/RunCyclesSection.tsx +298 -0
  509. package/src/modules/billing/components/TestRunActions.tsx +152 -0
  510. package/src/modules/billing/components/account/AccountItemsTab.tsx +192 -0
  511. package/src/modules/billing/components/account/AccountRunBillingDialog.tsx +147 -0
  512. package/src/modules/billing/components/account/AccountRunsTab.tsx +201 -0
  513. package/src/modules/billing/components/account/AccountStatementsTab.tsx +120 -0
  514. package/src/modules/billing/components/account/AccountSummaryCards.tsx +153 -0
  515. package/src/modules/billing/components/account/AccountUsageTab.tsx +122 -0
  516. package/src/modules/billing/components/account/AccountView.tsx +101 -0
  517. package/src/modules/billing/components/account/accountRow.ts +70 -0
  518. package/src/modules/billing/components/customerNames.ts +62 -0
  519. package/src/modules/billing/data/entities.ts +654 -5
  520. package/src/modules/billing/data/entityIds.ts +9 -0
  521. package/src/modules/billing/data/validators.ts +388 -8
  522. package/src/modules/billing/events.ts +56 -3
  523. package/src/modules/billing/i18n/en.json +457 -21
  524. package/src/modules/billing/i18n/pl.json +457 -21
  525. package/src/modules/billing/index.ts +6 -2
  526. package/src/modules/billing/lib/accountFinancials.ts +84 -0
  527. package/src/modules/billing/lib/accountLock.ts +32 -0
  528. package/src/modules/billing/lib/accountStatus.ts +11 -9
  529. package/src/modules/billing/lib/accountSummary.ts +172 -0
  530. package/src/modules/billing/lib/allocation.ts +515 -0
  531. package/src/modules/billing/lib/allocationEvents.ts +26 -0
  532. package/src/modules/billing/lib/billPeriod.ts +19 -0
  533. package/src/modules/billing/lib/billRunEngine.ts +350 -150
  534. package/src/modules/billing/lib/billingSettings.ts +145 -0
  535. package/src/modules/billing/lib/commandRoute.ts +112 -0
  536. package/src/modules/billing/lib/customerLookup.ts +157 -0
  537. package/src/modules/billing/lib/dateOnly.ts +22 -0
  538. package/src/modules/billing/lib/documentParties.ts +23 -0
  539. package/src/modules/billing/lib/documentTypes.ts +37 -0
  540. package/src/modules/billing/lib/dueDate.ts +26 -0
  541. package/src/modules/billing/lib/iban.ts +36 -0
  542. package/src/modules/billing/lib/invoiceDelivery.ts +197 -0
  543. package/src/modules/billing/lib/invoiceLanguages.ts +34 -0
  544. package/src/modules/billing/lib/invoiceWriter.ts +162 -148
  545. package/src/modules/billing/lib/itemBillingState.ts +67 -0
  546. package/src/modules/billing/lib/itemPriceChange.ts +56 -0
  547. package/src/modules/billing/lib/itemSelector.ts +4 -1
  548. package/src/modules/billing/lib/lineAmounts.ts +61 -0
  549. package/src/modules/billing/lib/memoGuards.ts +41 -0
  550. package/src/modules/billing/lib/navigation.ts +64 -0
  551. package/src/modules/billing/lib/numbering.ts +174 -0
  552. package/src/modules/billing/lib/overdue.ts +60 -0
  553. package/src/modules/billing/lib/paymentCsv.ts +171 -0
  554. package/src/modules/billing/lib/paymentGateway.ts +52 -0
  555. package/src/modules/billing/lib/paymentImport.ts +228 -0
  556. package/src/modules/billing/lib/paymentMatcher.ts +88 -0
  557. package/src/modules/billing/lib/pdf/attachmentStore.ts +60 -0
  558. package/src/modules/billing/lib/pdf/fonts/NotoSans-Bold.ttf +0 -0
  559. package/src/modules/billing/lib/pdf/fonts/NotoSans-Regular.ttf +0 -0
  560. package/src/modules/billing/lib/pdf/fonts/OFL.txt +93 -0
  561. package/src/modules/billing/lib/pdf/fonts/embeddedFonts.ts +9 -0
  562. package/src/modules/billing/lib/pdf/invoiceModel.ts +328 -0
  563. package/src/modules/billing/lib/pdf/invoicePdf.ts +216 -0
  564. package/src/modules/billing/lib/pdf/invoiceTemplate.ts +338 -0
  565. package/src/modules/billing/lib/prepaidConfig.ts +27 -36
  566. package/src/modules/billing/lib/prepaidConsume.ts +29 -6
  567. package/src/modules/billing/lib/prepaidPeriodClose.ts +407 -239
  568. package/src/modules/billing/lib/prepaidTaxPoint.ts +76 -0
  569. package/src/modules/billing/lib/seeds/demo.ts +79 -6
  570. package/src/modules/billing/lib/seeds/paymentsSample.ts +136 -0
  571. package/src/modules/billing/lib/seeds/sellerSettings.ts +78 -0
  572. package/src/modules/billing/lib/servicePeriods.ts +226 -0
  573. package/src/modules/billing/lib/testRunCleanup.ts +42 -0
  574. package/src/modules/billing/lib/topupCapture.ts +3 -4
  575. package/src/modules/billing/lib/topupReceipt.ts +70 -103
  576. package/src/modules/billing/lib/usageRater.ts +1 -1
  577. package/src/modules/billing/lib/usageRunner.ts +17 -5
  578. package/src/modules/billing/lib/vat.ts +56 -0
  579. package/src/modules/billing/migrations/Migration20261114000000_billing_customer_kind.ts +25 -0
  580. package/src/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.ts +37 -0
  581. package/src/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.ts +132 -0
  582. package/src/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.ts +58 -0
  583. package/src/modules/billing/migrations/Migration20261117000000_billing_payments.ts +97 -0
  584. package/src/modules/billing/migrations/Migration20261118000000_billing_memos.ts +21 -0
  585. package/src/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.ts +39 -0
  586. package/src/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.ts +35 -0
  587. package/src/modules/billing/setup.ts +11 -237
  588. package/src/modules/billing/subscribers/credit-memo-auto-send.ts +15 -0
  589. package/src/modules/billing/subscribers/debit-memo-apply-credit.ts +15 -0
  590. package/src/modules/billing/subscribers/debit-memo-auto-send.ts +15 -0
  591. package/src/modules/billing/subscribers/invoice-apply-credit.ts +39 -0
  592. package/src/modules/billing/subscribers/invoice-auto-send.ts +46 -0
  593. package/src/modules/billing/subscribers/payment-captured-to-paid.ts +84 -62
  594. package/src/modules/billing/subscribers/prepaid-topup-captured.ts +4 -8
  595. package/dist/modules/billing/lib/invoiceStatus.js +0 -38
  596. package/dist/modules/billing/lib/invoiceStatus.js.map +0 -7
  597. package/src/modules/billing/__tests__/lib/invoiceStatus.test.ts +0 -97
  598. package/src/modules/billing/lib/invoiceStatus.ts +0 -61
@@ -0,0 +1,549 @@
1
+ "use client"
2
+ import * as React from 'react'
3
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
4
+ import { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'
5
+ import { Button } from '@open-mercato/ui/primitives/button'
6
+ import { Input } from '@open-mercato/ui/primitives/input'
7
+ import { Textarea } from '@open-mercato/ui/primitives/textarea'
8
+ import { Switch } from '@open-mercato/ui/primitives/switch'
9
+ import { Tag } from '@open-mercato/ui/primitives/tag'
10
+ import { RadioGroup } from '@open-mercato/ui/primitives/radio'
11
+ import { RadioField } from '@open-mercato/ui/primitives/radio-field'
12
+ import { KbdShortcut } from '@open-mercato/ui/primitives/kbd'
13
+ import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
14
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
+ import { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
16
+ import { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'
17
+ import { DOCUMENT_SERIES, type DocumentSeries } from '../../../lib/numbering'
18
+
19
+ /**
20
+ * Billing settings (Spec 0) — per organization:
21
+ * - document number masks per series, with a live example of the next
22
+ * number and the counter scope (never / yearly / monthly reset);
23
+ * - prepaid defaults (moved from core `configs`);
24
+ * - read-only: whether online top-ups are available (optional gateway);
25
+ * - Spec B: seller block, payment instructions (bank / IBAN / BIC),
26
+ * default payment terms and VAT rate, invoice footer and logo. Posting
27
+ * is blocked until legal name, address, tax id and IBAN are filled — the
28
+ * page shows what is missing.
29
+ */
30
+
31
+ type Settings = {
32
+ id: string
33
+ lowBalanceThresholdDefault: number
34
+ nearLimitBufferDefault: number
35
+ statementEnabled: boolean
36
+ topupTaxRate: number
37
+ prepaidTaxPoint: 'topup' | 'consumption'
38
+ topupProvider: string
39
+ numberMasks: Record<DocumentSeries, string>
40
+ legalName: string
41
+ sellerAddress: SellerAddress | null
42
+ taxId: string
43
+ bankName: string
44
+ iban: string
45
+ bic: string
46
+ invoiceFooter: string
47
+ logoAttachmentId: string | null
48
+ defaultPaymentTermsDays: number
49
+ defaultTaxRate: number
50
+ }
51
+
52
+ type SellerAddress = { line1: string; line2?: string; city: string; postal_code: string; country: string }
53
+
54
+ const EMPTY_ADDRESS: SellerAddress = { line1: '', line2: '', city: '', postal_code: '', country: 'PL' }
55
+
56
+ type SettingsResponse = {
57
+ settings: Settings
58
+ capabilities: { gatewayTopups: boolean }
59
+ completeness?: { ready: boolean; missing: string[] }
60
+ }
61
+
62
+ /** Address is sent only when the required parts are filled; all blank clears it. */
63
+ function addressPayload(address: SellerAddress | null): SellerAddress | null | undefined {
64
+ if (!address) return null
65
+ const trimmed = {
66
+ line1: address.line1.trim(),
67
+ line2: (address.line2 ?? '').trim(),
68
+ city: address.city.trim(),
69
+ postal_code: address.postal_code.trim(),
70
+ country: address.country.trim().toUpperCase(),
71
+ }
72
+ if (!trimmed.line1 && !trimmed.city && !trimmed.postal_code) return null
73
+ const { line2, ...rest } = trimmed
74
+ return line2 ? { ...rest, line2 } : rest
75
+ }
76
+
77
+ const ATTACHMENT_ENTITY_ID = 'billing:billing_settings'
78
+
79
+ type Preview = { valid: boolean; errors: string[]; example?: string; scopeKey?: string }
80
+
81
+ function useMaskPreview(mask: string): Preview | null {
82
+ const [preview, setPreview] = React.useState<Preview | null>(null)
83
+ React.useEffect(() => {
84
+ let cancelled = false
85
+ const handle = setTimeout(() => {
86
+ void apiCall<Preview>(`/api/billing/settings/number-preview?mask=${encodeURIComponent(mask)}`).then((call) => {
87
+ if (!cancelled) setPreview(call.ok ? call.result ?? null : null)
88
+ })
89
+ }, 250)
90
+ return () => {
91
+ cancelled = true
92
+ clearTimeout(handle)
93
+ }
94
+ }, [mask])
95
+ return preview
96
+ }
97
+
98
+ function MaskField({
99
+ series,
100
+ value,
101
+ initial,
102
+ onChange,
103
+ }: {
104
+ series: DocumentSeries
105
+ value: string
106
+ initial: string
107
+ onChange: (next: string) => void
108
+ }) {
109
+ const t = useT()
110
+ const preview = useMaskPreview(value)
111
+ const seriesLabel: Record<DocumentSeries, string> = {
112
+ invoice: t('billing.settings.series.invoice', 'Invoices and debit memos'),
113
+ credit_memo: t('billing.settings.series.credit_memo', 'Credit memos'),
114
+ receipt: t('billing.settings.series.receipt', 'Top-up receipts'),
115
+ }
116
+ const scopeLabel = (scopeKey: string | undefined) =>
117
+ !scopeKey
118
+ ? t('billing.settings.mask.scope.none', 'Counter never resets')
119
+ : scopeKey.length === 4
120
+ ? t('billing.settings.mask.scope.year', 'Counter resets every year')
121
+ : t('billing.settings.mask.scope.month', 'Counter resets every month')
122
+ return (
123
+ <label className="flex flex-col gap-1 text-sm">
124
+ <span className="text-muted-foreground">{seriesLabel[series]}</span>
125
+ <Input value={value} maxLength={64} onChange={(event) => onChange(event.currentTarget.value)} />
126
+ {preview?.valid ? (
127
+ <span className="text-xs text-muted-foreground">
128
+ {t('billing.settings.mask.example', 'Next number looks like')} <span className="font-mono">{preview.example}</span>
129
+ {' · '}
130
+ {scopeLabel(preview.scopeKey)}
131
+ </span>
132
+ ) : preview ? (
133
+ <span className="text-xs text-destructive">
134
+ {preview.errors.map((code) => t(`billing.settings.mask.error.${code}`, code)).join(' ')}
135
+ </span>
136
+ ) : null}
137
+ {value !== initial ? (
138
+ <span className="text-xs text-muted-foreground">
139
+ {t(
140
+ 'billing.settings.mask.change_warning',
141
+ 'Changing the mask mid-period starts a new counter; a number that was already issued is rejected at post.',
142
+ )}
143
+ </span>
144
+ ) : null}
145
+ </label>
146
+ )
147
+ }
148
+
149
+ export default function BillingSettingsPage() {
150
+ const t = useT()
151
+ const [loaded, setLoaded] = React.useState<SettingsResponse | null>(null)
152
+ const [values, setValues] = React.useState<Settings | null>(null)
153
+ const [error, setError] = React.useState<string | null>(null)
154
+ const [saving, setSaving] = React.useState(false)
155
+
156
+ const load = React.useCallback(async () => {
157
+ setError(null)
158
+ try {
159
+ const result = await readApiResultOrThrow<SettingsResponse>('/api/billing/settings')
160
+ setLoaded(result)
161
+ setValues(result.settings)
162
+ } catch (err) {
163
+ setError(normalizeCrudServerError(err).message || t('billing.settings.error.load', 'Could not load billing settings'))
164
+ }
165
+ }, [t])
166
+
167
+ React.useEffect(() => {
168
+ void load()
169
+ }, [load])
170
+
171
+ const set = <K extends keyof Settings>(key: K, value: Settings[K]) =>
172
+ setValues((prev) => (prev ? { ...prev, [key]: value } : prev))
173
+
174
+ const fieldLabel = React.useCallback(
175
+ (field: string): string => {
176
+ const labels: Record<string, string> = {
177
+ legalName: t('billing.settings.seller.legal_name', 'Legal name'),
178
+ sellerAddress: t('billing.settings.seller.address', 'Address'),
179
+ taxId: t('billing.settings.seller.tax_id', 'Tax ID'),
180
+ iban: 'IBAN',
181
+ bic: 'BIC',
182
+ bankName: t('billing.settings.payment.bank_name', 'Bank'),
183
+ topupProvider: t('billing.settings.prepaid.topup_provider', 'Top-up payment provider'),
184
+ topupTaxRate: t('billing.settings.prepaid.topup_tax_rate', 'VAT on top-up receipts (%)'),
185
+ defaultPaymentTermsDays: t('billing.settings.payment.terms', 'Default payment terms (days)'),
186
+ defaultTaxRate: t('billing.settings.payment.tax_rate', 'Default VAT rate (%)'),
187
+ numberMasks: t('billing.settings.numbering.title', 'Document numbering'),
188
+ }
189
+ const root = field.split('.')[0]
190
+ const rest = field.slice(root.length + 1)
191
+ return `${labels[root] ?? root}${rest ? ` (${rest})` : ''}`
192
+ },
193
+ [t],
194
+ )
195
+
196
+ const handleSave = React.useCallback(async () => {
197
+ if (!values) return
198
+ setSaving(true)
199
+ try {
200
+ await apiCallOrThrow('/api/billing/settings', {
201
+ method: 'PUT',
202
+ headers: { 'Content-Type': 'application/json' },
203
+ body: JSON.stringify({ ...values, sellerAddress: addressPayload(values.sellerAddress) }),
204
+ })
205
+ flash(t('billing.settings.saved', 'Billing settings saved'), 'success')
206
+ await load()
207
+ } catch (err) {
208
+ // Name the failing fields — the API's top-level message is just
209
+ // "Invalid input". Codes such as `billing.settings.error.iban_invalid`
210
+ // and mask error codes are translated; anything else is shown as sent.
211
+ const { message, fieldErrors } = normalizeCrudServerError(err)
212
+ const details = Object.entries(fieldErrors ?? {})
213
+ .map(([field, fieldMessage]) => {
214
+ const text = String(fieldMessage)
215
+ const translated = text.startsWith('billing.') ? t(text, text) : text
216
+ return `${fieldLabel(field)}: ${translated}`
217
+ })
218
+ .join('; ')
219
+ flash(
220
+ details
221
+ ? `${t('billing.settings.error.fields', 'Check these fields')} — ${details}`
222
+ : message || t('billing.settings.error.save', 'Saving failed'),
223
+ 'error',
224
+ )
225
+ } finally {
226
+ setSaving(false)
227
+ }
228
+ }, [fieldLabel, load, t, values])
229
+
230
+ const [uploading, setUploading] = React.useState(false)
231
+ const handleLogoUpload = React.useCallback(
232
+ async (file: File | null) => {
233
+ if (!file || !values) return
234
+ setUploading(true)
235
+ try {
236
+ const form = new FormData()
237
+ form.set('entityId', ATTACHMENT_ENTITY_ID)
238
+ form.set('recordId', loaded?.settings.id ?? 'settings')
239
+ form.set('file', file)
240
+ const call = await apiCall<{ item?: { id: string }; error?: string }>('/api/attachments', {
241
+ method: 'POST',
242
+ body: form,
243
+ })
244
+ if (!call.ok || !call.result?.item?.id) {
245
+ throw new Error(call.result?.error || t('billing.settings.logo.upload_failed', 'Logo upload failed'))
246
+ }
247
+ set('logoAttachmentId', call.result.item.id)
248
+ } catch (err) {
249
+ flash(err instanceof Error ? err.message : t('billing.settings.logo.upload_failed', 'Logo upload failed'), 'error')
250
+ } finally {
251
+ setUploading(false)
252
+ }
253
+ },
254
+ [loaded?.settings.id, t, values],
255
+ )
256
+
257
+ const address = values?.sellerAddress ?? EMPTY_ADDRESS
258
+ const setAddress = (key: keyof SellerAddress, value: string) =>
259
+ set('sellerAddress', { ...(values?.sellerAddress ?? EMPTY_ADDRESS), [key]: value })
260
+
261
+ const missingLabels: Record<string, string> = {
262
+ legalName: t('billing.settings.seller.legal_name', 'Legal name'),
263
+ sellerAddress: t('billing.settings.seller.address', 'Address'),
264
+ taxId: t('billing.settings.seller.tax_id', 'Tax ID'),
265
+ iban: t('billing.settings.payment.iban', 'IBAN'),
266
+ }
267
+
268
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
269
+ if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
270
+ event.preventDefault()
271
+ void handleSave()
272
+ } else if (event.key === 'Escape' && loaded) {
273
+ event.preventDefault()
274
+ setValues(loaded.settings)
275
+ }
276
+ }
277
+
278
+ return (
279
+ <Page>
280
+ <PageHeader title={t('billing.settings.title', 'Billing')} />
281
+ <PageBody>
282
+ {error ? (
283
+ <ErrorMessage label={error} />
284
+ ) : !values || !loaded ? (
285
+ <LoadingMessage label={t('billing.settings.loading', 'Loading billing settings…')} />
286
+ ) : (
287
+ <div className="flex flex-col gap-6" onKeyDown={handleKeyDown}>
288
+ {loaded.completeness && !loaded.completeness.ready ? (
289
+ <div className="rounded-lg border border-status-warning-border bg-status-warning-bg p-4 text-sm">
290
+ <div className="font-medium">
291
+ {t('billing.settings.incomplete.title', 'Invoices cannot be posted yet')}
292
+ </div>
293
+ <div className="text-muted-foreground">
294
+ {t('billing.settings.incomplete.help', 'Fill in the seller details printed on every invoice:')}{' '}
295
+ {loaded.completeness.missing.map((field) => missingLabels[field] ?? field).join(', ')}
296
+ </div>
297
+ </div>
298
+ ) : null}
299
+
300
+ <section className="rounded-lg border bg-card p-6 grid grid-cols-1 sm:grid-cols-2 gap-4">
301
+ <h2 className="text-base font-semibold sm:col-span-2">{t('billing.settings.seller.title', 'Seller')}</h2>
302
+ <label className="flex flex-col gap-1 text-sm sm:col-span-2">
303
+ <span className="text-muted-foreground">{t('billing.settings.seller.legal_name', 'Legal name')} *</span>
304
+ <Input value={values.legalName} maxLength={255} onChange={(e) => set('legalName', e.currentTarget.value)} />
305
+ </label>
306
+ <label className="flex flex-col gap-1 text-sm sm:col-span-2">
307
+ <span className="text-muted-foreground">{t('billing.settings.seller.line1', 'Address line 1')} *</span>
308
+ <Input value={address.line1} onChange={(e) => setAddress('line1', e.currentTarget.value)} />
309
+ </label>
310
+ <label className="flex flex-col gap-1 text-sm sm:col-span-2">
311
+ <span className="text-muted-foreground">{t('billing.settings.seller.line2', 'Address line 2')}</span>
312
+ <Input value={address.line2 ?? ''} onChange={(e) => setAddress('line2', e.currentTarget.value)} />
313
+ </label>
314
+ <label className="flex flex-col gap-1 text-sm">
315
+ <span className="text-muted-foreground">{t('billing.settings.seller.postal_code', 'Postal code')} *</span>
316
+ <Input value={address.postal_code} onChange={(e) => setAddress('postal_code', e.currentTarget.value)} />
317
+ </label>
318
+ <label className="flex flex-col gap-1 text-sm">
319
+ <span className="text-muted-foreground">{t('billing.settings.seller.city', 'City')} *</span>
320
+ <Input value={address.city} onChange={(e) => setAddress('city', e.currentTarget.value)} />
321
+ </label>
322
+ <label className="flex flex-col gap-1 text-sm">
323
+ <span className="text-muted-foreground">{t('billing.settings.seller.country', 'Country (ISO code)')} *</span>
324
+ <Input value={address.country} maxLength={3} onChange={(e) => setAddress('country', e.currentTarget.value)} />
325
+ </label>
326
+ <label className="flex flex-col gap-1 text-sm">
327
+ <span className="text-muted-foreground">{t('billing.settings.seller.tax_id', 'Tax ID')} *</span>
328
+ <Input value={values.taxId} maxLength={64} onChange={(e) => set('taxId', e.currentTarget.value)} />
329
+ </label>
330
+ </section>
331
+
332
+ <section className="rounded-lg border bg-card p-6 grid grid-cols-1 sm:grid-cols-2 gap-4">
333
+ <h2 className="text-base font-semibold sm:col-span-2">{t('billing.settings.payment.title', 'Payment and invoicing')}</h2>
334
+ <label className="flex flex-col gap-1 text-sm sm:col-span-2">
335
+ <span className="text-muted-foreground">{t('billing.settings.payment.bank_name', 'Bank name')}</span>
336
+ <Input value={values.bankName} maxLength={255} onChange={(e) => set('bankName', e.currentTarget.value)} />
337
+ </label>
338
+ <label className="flex flex-col gap-1 text-sm">
339
+ <span className="text-muted-foreground">{t('billing.settings.payment.iban', 'IBAN')} *</span>
340
+ <Input value={values.iban} className="font-mono" onChange={(e) => set('iban', e.currentTarget.value)} />
341
+ </label>
342
+ <label className="flex flex-col gap-1 text-sm">
343
+ <span className="text-muted-foreground">{t('billing.settings.payment.bic', 'BIC / SWIFT')}</span>
344
+ <Input value={values.bic} className="font-mono" maxLength={11} onChange={(e) => set('bic', e.currentTarget.value)} />
345
+ </label>
346
+ <label className="flex flex-col gap-1 text-sm">
347
+ <span className="text-muted-foreground">{t('billing.settings.payment.terms', 'Default payment terms (days)')}</span>
348
+ <Input
349
+ type="number"
350
+ min="0"
351
+ max="365"
352
+ step="1"
353
+ value={String(values.defaultPaymentTermsDays)}
354
+ onChange={(e) => set('defaultPaymentTermsDays', Number.parseInt(e.currentTarget.value, 10) || 0)}
355
+ />
356
+ </label>
357
+ <label className="flex flex-col gap-1 text-sm">
358
+ <span className="text-muted-foreground">{t('billing.settings.payment.tax_rate', 'Default VAT rate (%)')}</span>
359
+ <Input
360
+ type="number"
361
+ min="0"
362
+ max="100"
363
+ step="0.01"
364
+ value={String(values.defaultTaxRate)}
365
+ onChange={(e) => set('defaultTaxRate', Number(e.currentTarget.value) || 0)}
366
+ />
367
+ <span className="text-xs text-muted-foreground">
368
+ {t('billing.settings.payment.tax_rate.help', 'Applied to new billing items only; existing items keep their rate.')}
369
+ </span>
370
+ </label>
371
+ <label className="flex flex-col gap-1 text-sm sm:col-span-2">
372
+ <span className="text-muted-foreground">{t('billing.settings.document.footer', 'Invoice footer')}</span>
373
+ <Textarea
374
+ rows={2}
375
+ maxLength={2000}
376
+ value={values.invoiceFooter}
377
+ onChange={(e) => set('invoiceFooter', e.currentTarget.value)}
378
+ />
379
+ <span className="text-xs text-muted-foreground">
380
+ {t('billing.settings.document.footer.help', 'Registration court, share capital and similar legal notes.')}
381
+ </span>
382
+ </label>
383
+ <div className="flex flex-col gap-2 text-sm sm:col-span-2">
384
+ <span className="text-muted-foreground">{t('billing.settings.document.logo', 'Logo (PNG or JPEG)')}</span>
385
+ {values.logoAttachmentId ? (
386
+ <div className="flex items-center gap-3">
387
+ <img
388
+ src={`/api/attachments/file/${encodeURIComponent(values.logoAttachmentId)}`}
389
+ alt={t('billing.settings.document.logo', 'Logo (PNG or JPEG)')}
390
+ className="h-12 max-w-[200px] object-contain rounded border bg-background"
391
+ />
392
+ <Button type="button" variant="outline" size="sm" onClick={() => set('logoAttachmentId', null)}>
393
+ {t('billing.settings.document.logo.remove', 'Remove')}
394
+ </Button>
395
+ </div>
396
+ ) : (
397
+ <span className="text-xs text-muted-foreground">
398
+ {t('billing.settings.document.logo.fallback', 'No logo — the organization logo is used when set.')}
399
+ </span>
400
+ )}
401
+ <Input
402
+ type="file"
403
+ accept="image/png,image/jpeg"
404
+ disabled={uploading}
405
+ onChange={(e) => void handleLogoUpload(e.currentTarget.files?.[0] ?? null)}
406
+ />
407
+ </div>
408
+ </section>
409
+
410
+ <section className="rounded-lg border bg-card p-6 flex flex-col gap-4">
411
+ <div>
412
+ <h2 className="text-base font-semibold">{t('billing.settings.numbering.title', 'Document numbering')}</h2>
413
+ <p className="text-sm text-muted-foreground">
414
+ {t(
415
+ 'billing.settings.numbering.help',
416
+ 'Use date tokens and exactly one counter token. The counter token decides when numbering restarts. Add :N to zero-pad the counter.',
417
+ )}
418
+ </p>
419
+ {/* Tokens are rendered literally — never through i18n interpolation. */}
420
+ <ul className="mt-2 grid grid-cols-1 sm:grid-cols-2 gap-1 text-xs">
421
+ {[
422
+ ['{YYYY} {YY} {MM} {DD}', t('billing.settings.token.date', 'Issue date parts')],
423
+ ['{SEQUENCE_NUM}', t('billing.settings.mask.scope.none', 'Counter never resets')],
424
+ ['{SEQUENCE_NUM_YEAR}', t('billing.settings.mask.scope.year', 'Counter resets every year')],
425
+ ['{SEQUENCE_NUM_MONTH}', t('billing.settings.mask.scope.month', 'Counter resets every month')],
426
+ [':N ({SEQUENCE_NUM_MONTH:4} → 0007)', t('billing.settings.token.pad', 'Zero-pad the counter to N digits')],
427
+ ].map(([token, label]) => (
428
+ <li key={token}>
429
+ <span className="font-mono">{token}</span> — {label}
430
+ </li>
431
+ ))}
432
+ </ul>
433
+ </div>
434
+ {DOCUMENT_SERIES.map((series) => (
435
+ <MaskField
436
+ key={series}
437
+ series={series}
438
+ value={values.numberMasks[series]}
439
+ initial={loaded.settings.numberMasks[series]}
440
+ onChange={(next) => set('numberMasks', { ...values.numberMasks, [series]: next })}
441
+ />
442
+ ))}
443
+ </section>
444
+
445
+ <section className="rounded-lg border bg-card p-6 grid grid-cols-1 sm:grid-cols-2 gap-4">
446
+ <h2 className="text-base font-semibold sm:col-span-2">{t('billing.settings.prepaid.title', 'Prepaid defaults')}</h2>
447
+ <label className="flex flex-col gap-1 text-sm">
448
+ <span className="text-muted-foreground">{t('billing.settings.prepaid.low_balance', 'Low-balance threshold')}</span>
449
+ <Input
450
+ type="number"
451
+ min="0"
452
+ step="0.01"
453
+ value={String(values.lowBalanceThresholdDefault)}
454
+ onChange={(e) => set('lowBalanceThresholdDefault', Number(e.currentTarget.value) || 0)}
455
+ />
456
+ </label>
457
+ <label className="flex flex-col gap-1 text-sm">
458
+ <span className="text-muted-foreground">{t('billing.settings.prepaid.near_limit', 'Near credit-limit buffer')}</span>
459
+ <Input
460
+ type="number"
461
+ min="0"
462
+ step="0.01"
463
+ value={String(values.nearLimitBufferDefault)}
464
+ onChange={(e) => set('nearLimitBufferDefault', Number(e.currentTarget.value) || 0)}
465
+ />
466
+ </label>
467
+ <div className="flex flex-col gap-2 text-sm sm:col-span-2">
468
+ <span className="text-muted-foreground">{t('billing.settings.prepaid.tax_point', 'When is VAT charged on prepaid accounts?')}</span>
469
+ <RadioGroup
470
+ value={values.prepaidTaxPoint ?? 'topup'}
471
+ onValueChange={(next) => set('prepaidTaxPoint', next === 'consumption' ? 'consumption' : 'topup')}
472
+ className="grid grid-cols-1 gap-3 sm:grid-cols-2"
473
+ >
474
+ <RadioField
475
+ value="topup"
476
+ label={t('billing.settings.prepaid.tax_point.topup', 'At top-up')}
477
+ description={t(
478
+ 'billing.settings.prepaid.tax_point.topup_help',
479
+ 'The top-up receipt includes VAT. Charges take the item amount from the balance as entered; item VAT rates do not apply.',
480
+ )}
481
+ />
482
+ <RadioField
483
+ value="consumption"
484
+ label={t('billing.settings.prepaid.tax_point.consumption', 'At billing')}
485
+ description={t(
486
+ 'billing.settings.prepaid.tax_point.consumption_help',
487
+ 'Top-ups carry no VAT. Each charge takes its gross amount (at the item VAT rate) from the balance; statements show the VAT. Check with your accountant.',
488
+ )}
489
+ />
490
+ </RadioGroup>
491
+ {(values.prepaidTaxPoint ?? 'topup') !== (loaded.settings.prepaidTaxPoint ?? 'topup') ? (
492
+ <div className="rounded-md border border-status-warning-border bg-status-warning-bg px-3 py-2 text-xs text-status-warning-text">
493
+ {t(
494
+ 'billing.settings.prepaid.tax_point.change_warning',
495
+ 'Applies to new top-ups and charges only. Balances topped up under the other setting keep their tax treatment; switching from top-up to billing taxes the remaining balance again when it is used.',
496
+ )}
497
+ </div>
498
+ ) : null}
499
+ </div>
500
+ {(values.prepaidTaxPoint ?? 'topup') === 'topup' ? (
501
+ <label className="flex flex-col gap-1 text-sm">
502
+ <span className="text-muted-foreground">{t('billing.settings.prepaid.topup_tax_rate', 'VAT on top-up receipts (%)')}</span>
503
+ <Input
504
+ type="number"
505
+ min="0"
506
+ max="100"
507
+ step="0.01"
508
+ value={String(values.topupTaxRate)}
509
+ onChange={(e) => set('topupTaxRate', Number(e.currentTarget.value) || 0)}
510
+ />
511
+ </label>
512
+ ) : null}
513
+ <label className="flex flex-col gap-1 text-sm">
514
+ <span className="text-muted-foreground">{t('billing.settings.prepaid.topup_provider', 'Top-up payment provider')}</span>
515
+ <Input value={values.topupProvider} onChange={(e) => set('topupProvider', e.currentTarget.value)} />
516
+ </label>
517
+ <div className="flex items-center justify-between gap-3 sm:col-span-2">
518
+ <span className="text-sm">{t('billing.settings.prepaid.statements', 'Generate consumption statements at period close')}</span>
519
+ <Switch checked={values.statementEnabled} onCheckedChange={(next) => set('statementEnabled', next)} />
520
+ </div>
521
+ <div className="flex items-center gap-2 text-sm sm:col-span-2">
522
+ <span className="text-muted-foreground">{t('billing.settings.prepaid.gateway', 'Online top-ups')}</span>
523
+ {loaded.capabilities.gatewayTopups ? (
524
+ <Tag variant="success">{t('billing.settings.prepaid.gateway.available', 'Available')}</Tag>
525
+ ) : (
526
+ <Tag variant="neutral">
527
+ {t('billing.settings.prepaid.gateway.unavailable', 'Unavailable — payment gateways module not installed')}
528
+ </Tag>
529
+ )}
530
+ </div>
531
+ </section>
532
+
533
+ <div className="flex items-center justify-end gap-3">
534
+ <span className="text-xs text-muted-foreground">
535
+ <KbdShortcut keys={['Ctrl/⌘', 'Enter']} /> {t('billing.settings.save_hint', 'to save')}
536
+ </span>
537
+ <Button type="button" variant="outline" onClick={() => setValues(loaded.settings)} disabled={saving}>
538
+ {t('billing.common.cancel', 'Cancel')}
539
+ </Button>
540
+ <Button type="button" onClick={() => void handleSave()} disabled={saving}>
541
+ {saving ? t('billing.settings.saving', 'Saving…') : t('billing.common.save', 'Save')}
542
+ </Button>
543
+ </div>
544
+ </div>
545
+ )}
546
+ </PageBody>
547
+ </Page>
548
+ )
549
+ }
@@ -86,4 +86,34 @@ const seed: ModuleCli = {
86
86
  },
87
87
  }
88
88
 
89
- export default [seed]
89
+ /**
90
+ * `mercato billing payments-sample [--out <file>] [--tenant <id>] [--org <id>]`
91
+ *
92
+ * Spec C demo: writes a bank CSV built from the organization's posted,
93
+ * unpaid invoices (and a top-up receipt, if any) that shows every matcher
94
+ * outcome after one upload on Billing → Payments. Run a real bill run and
95
+ * post a few invoices first.
96
+ */
97
+ const paymentsSample: ModuleCli = {
98
+ command: 'payments-sample',
99
+ async run(rest) {
100
+ const args = parseArgs(rest)
101
+ const container = await createRequestContainer()
102
+ const em = container.resolve<EntityManager>('em')
103
+ const scope = await resolveScope(em, args)
104
+ if (!scope) {
105
+ console.error('[billing payments-sample] No organization found. Pass --org <id>.')
106
+ return
107
+ }
108
+ const { buildPaymentsSampleCsv, loadSampleSources } = await import('./lib/seeds/paymentsSample')
109
+ const { writeFile } = await import('node:fs/promises')
110
+ const sources = await loadSampleSources(em, scope)
111
+ const out = typeof args.out === 'string' ? args.out : 'payments-sample.csv'
112
+ const sample = buildPaymentsSampleCsv({ ...sources, date: new Date().toISOString().slice(0, 10) })
113
+ await writeFile(out, sample.csv, 'utf8')
114
+ console.log(`[billing payments-sample] wrote ${sample.rows} rows to ${out}`)
115
+ for (const note of sample.missing) console.log(` · skipped ${note}`)
116
+ },
117
+ }
118
+
119
+ export default [seed, paymentsSample]
@@ -66,6 +66,7 @@ const createAccountCommand: CommandHandler<BillingAccountCreateInput, { id: stri
66
66
  organizationId: parsed.organizationId,
67
67
  tenantId: parsed.tenantId,
68
68
  customerId: parsed.customerId,
69
+ customerKind: parsed.customerKind,
69
70
  name: parsed.name,
70
71
  currencyCode: parsed.currencyCode,
71
72
  billCycle: parsed.billCycle,
@@ -79,6 +80,9 @@ const createAccountCommand: CommandHandler<BillingAccountCreateInput, { id: stri
79
80
  billingMode,
80
81
  creditLimit:
81
82
  parsed.creditLimit !== undefined ? formatMoney(parsed.creditLimit) : '0',
83
+ // Spec B: null = organization default terms.
84
+ paymentTermsDays: parsed.paymentTermsDays ?? null,
85
+ autoSendInvoice: parsed.autoSendInvoice ?? false,
82
86
  isActive: parsed.isActive ?? true,
83
87
  createdAt: now,
84
88
  updatedAt: now,
@@ -153,6 +157,8 @@ const updateAccountCommand: CommandHandler<BillingAccountUpdateInput, { id: stri
153
157
  if (parsed.isActive !== undefined) record.isActive = parsed.isActive
154
158
  // SPEC-002: credit_limit is editable in both modes (billing_mode is not).
155
159
  if (parsed.creditLimit !== undefined) record.creditLimit = formatMoney(parsed.creditLimit)
160
+ if (parsed.paymentTermsDays !== undefined) record.paymentTermsDays = parsed.paymentTermsDays
161
+ if (parsed.autoSendInvoice !== undefined) record.autoSendInvoice = parsed.autoSendInvoice
156
162
 
157
163
  record.updatedAt = new Date()
158
164
 
@@ -14,3 +14,6 @@ import './invoices'
14
14
  import './topups'
15
15
  import './transactions'
16
16
  import './statements'
17
+ import './settings'
18
+ import './payments'
19
+ import './memos'