@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.
- package/CHANGELOG.md +90 -0
- package/dist/modules/billing/acl.js +13 -1
- package/dist/modules/billing/acl.js.map +2 -2
- package/dist/modules/billing/api/accounts/[id]/summary/route.js +52 -0
- package/dist/modules/billing/api/accounts/[id]/summary/route.js.map +7 -0
- package/dist/modules/billing/api/accounts/route.js +46 -4
- package/dist/modules/billing/api/accounts/route.js.map +2 -2
- package/dist/modules/billing/api/export/account/[id]/route.js +11 -7
- package/dist/modules/billing/api/export/account/[id]/route.js.map +2 -2
- package/dist/modules/billing/api/invoices/[id]/memos/route.js +54 -0
- package/dist/modules/billing/api/invoices/[id]/memos/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/[id]/pdf/route.js +69 -0
- package/dist/modules/billing/api/invoices/[id]/pdf/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/[id]/route.js +44 -24
- package/dist/modules/billing/api/invoices/[id]/route.js.map +2 -2
- package/dist/modules/billing/api/invoices/[id]/send/route.js +107 -0
- package/dist/modules/billing/api/invoices/[id]/send/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/add-line/route.js +2 -2
- package/dist/modules/billing/api/invoices/add-line/route.js.map +2 -2
- package/dist/modules/billing/api/invoices/delete-draft/route.js +33 -0
- package/dist/modules/billing/api/invoices/delete-draft/route.js.map +7 -0
- package/dist/modules/billing/api/invoices/edit-line/route.js +2 -2
- package/dist/modules/billing/api/invoices/edit-line/route.js.map +2 -2
- package/dist/modules/billing/api/invoices/post/route.js +2 -2
- package/dist/modules/billing/api/invoices/post/route.js.map +2 -2
- package/dist/modules/billing/api/invoices/remove-line/route.js +2 -2
- package/dist/modules/billing/api/invoices/remove-line/route.js.map +2 -2
- package/dist/modules/billing/api/invoices/route.js +45 -46
- package/dist/modules/billing/api/invoices/route.js.map +2 -2
- package/dist/modules/billing/api/items/bulk/route.js +1 -1
- package/dist/modules/billing/api/items/bulk/route.js.map +2 -2
- package/dist/modules/billing/api/items/change-price/route.js +44 -0
- package/dist/modules/billing/api/items/change-price/route.js.map +7 -0
- package/dist/modules/billing/api/items/route.js +19 -7
- package/dist/modules/billing/api/items/route.js.map +2 -2
- package/dist/modules/billing/api/payment-imports/[id]/route.js +49 -0
- package/dist/modules/billing/api/payment-imports/[id]/route.js.map +7 -0
- package/dist/modules/billing/api/payment-imports/route.js +57 -0
- package/dist/modules/billing/api/payment-imports/route.js.map +7 -0
- package/dist/modules/billing/api/payments/[id]/route.js +60 -0
- package/dist/modules/billing/api/payments/[id]/route.js.map +7 -0
- package/dist/modules/billing/api/payments/allocate/route.js +35 -0
- package/dist/modules/billing/api/payments/allocate/route.js.map +7 -0
- package/dist/modules/billing/api/payments/assign/route.js +36 -0
- package/dist/modules/billing/api/payments/assign/route.js.map +7 -0
- package/dist/modules/billing/api/payments/convert-to-topup/route.js +35 -0
- package/dist/modules/billing/api/payments/convert-to-topup/route.js.map +7 -0
- package/dist/modules/billing/api/payments/ignore/route.js +35 -0
- package/dist/modules/billing/api/payments/ignore/route.js.map +7 -0
- package/dist/modules/billing/api/payments/import/route.js +63 -0
- package/dist/modules/billing/api/payments/import/route.js.map +7 -0
- package/dist/modules/billing/api/payments/restore/route.js +35 -0
- package/dist/modules/billing/api/payments/restore/route.js.map +7 -0
- package/dist/modules/billing/api/payments/route.js +114 -0
- package/dist/modules/billing/api/payments/route.js.map +7 -0
- package/dist/modules/billing/api/payments/unallocate/route.js +35 -0
- package/dist/modules/billing/api/payments/unallocate/route.js.map +7 -0
- package/dist/modules/billing/api/payments/unassign/route.js +35 -0
- package/dist/modules/billing/api/payments/unassign/route.js.map +7 -0
- package/dist/modules/billing/api/run-outcomes/route.js +43 -6
- package/dist/modules/billing/api/run-outcomes/route.js.map +2 -2
- package/dist/modules/billing/api/runs/[id]/post-drafts/route.js +61 -0
- package/dist/modules/billing/api/runs/[id]/post-drafts/route.js.map +7 -0
- package/dist/modules/billing/api/runs/[id]/route.js +53 -0
- package/dist/modules/billing/api/runs/[id]/route.js.map +7 -0
- package/dist/modules/billing/api/runs/retry-failed/route.js +1 -1
- package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
- package/dist/modules/billing/api/runs/route.js +9 -3
- package/dist/modules/billing/api/runs/route.js.map +2 -2
- package/dist/modules/billing/api/settings/number-preview/route.js +42 -0
- package/dist/modules/billing/api/settings/number-preview/route.js.map +7 -0
- package/dist/modules/billing/api/settings/route.js +92 -0
- package/dist/modules/billing/api/settings/route.js.map +7 -0
- package/dist/modules/billing/api/statements/route.js +3 -0
- package/dist/modules/billing/api/statements/route.js.map +2 -2
- package/dist/modules/billing/api/test-invoices/wipe/route.js +2 -2
- package/dist/modules/billing/api/test-invoices/wipe/route.js.map +2 -2
- package/dist/modules/billing/backend/billing/accounts/[id]/page.js +121 -232
- package/dist/modules/billing/backend/billing/accounts/[id]/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/accounts/create/page.js +6 -0
- package/dist/modules/billing/backend/billing/accounts/create/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/accounts/page.js +63 -4
- package/dist/modules/billing/backend/billing/accounts/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/invoices/[id]/page.js +297 -65
- package/dist/modules/billing/backend/billing/invoices/[id]/page.js.map +3 -3
- package/dist/modules/billing/backend/billing/invoices/page.js +6 -172
- package/dist/modules/billing/backend/billing/invoices/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/items/[id]/page.js +51 -7
- package/dist/modules/billing/backend/billing/items/[id]/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/items/create/page.js +9 -6
- package/dist/modules/billing/backend/billing/items/create/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/items/page.js +8 -2
- package/dist/modules/billing/backend/billing/items/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/payments/[id]/page.js +297 -0
- package/dist/modules/billing/backend/billing/payments/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js +21 -0
- package/dist/modules/billing/backend/billing/payments/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js +74 -0
- package/dist/modules/billing/backend/billing/payments/imports/[id]/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js +21 -0
- package/dist/modules/billing/backend/billing/payments/imports/[id]/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/payments/page.js +58 -0
- package/dist/modules/billing/backend/billing/payments/page.js.map +7 -0
- package/dist/modules/billing/backend/billing/payments/page.meta.js +18 -0
- package/dist/modules/billing/backend/billing/payments/page.meta.js.map +7 -0
- package/dist/modules/billing/backend/billing/runs/[id]/page.js +49 -14
- package/dist/modules/billing/backend/billing/runs/[id]/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/runs/page.js +58 -7
- package/dist/modules/billing/backend/billing/runs/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/statements/[id]/page.js +61 -6
- package/dist/modules/billing/backend/billing/statements/[id]/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/statements/page.js +1 -1
- package/dist/modules/billing/backend/billing/statements/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/topups/page.js +1 -1
- package/dist/modules/billing/backend/billing/topups/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/transactions/page.js +1 -1
- package/dist/modules/billing/backend/billing/transactions/page.js.map +2 -2
- package/dist/modules/billing/backend/billing/usage/create/page.js +3 -2
- package/dist/modules/billing/backend/billing/usage/create/page.js.map +2 -2
- package/dist/modules/billing/backend/config/billing/page.js +472 -0
- package/dist/modules/billing/backend/config/billing/page.js.map +7 -0
- package/dist/modules/billing/backend/config/billing/page.meta.js +19 -0
- package/dist/modules/billing/backend/config/billing/page.meta.js.map +7 -0
- package/dist/modules/billing/cli.js +22 -1
- package/dist/modules/billing/cli.js.map +2 -2
- package/dist/modules/billing/commands/accounts.js +6 -0
- package/dist/modules/billing/commands/accounts.js.map +2 -2
- package/dist/modules/billing/commands/index.js +3 -0
- package/dist/modules/billing/commands/index.js.map +2 -2
- package/dist/modules/billing/commands/invoices.js +269 -167
- package/dist/modules/billing/commands/invoices.js.map +2 -2
- package/dist/modules/billing/commands/items.js +108 -0
- package/dist/modules/billing/commands/items.js.map +2 -2
- package/dist/modules/billing/commands/memos.js +179 -0
- package/dist/modules/billing/commands/memos.js.map +7 -0
- package/dist/modules/billing/commands/payments.js +379 -0
- package/dist/modules/billing/commands/payments.js.map +7 -0
- package/dist/modules/billing/commands/runs.js +151 -4
- package/dist/modules/billing/commands/runs.js.map +2 -2
- package/dist/modules/billing/commands/settings.js +59 -0
- package/dist/modules/billing/commands/settings.js.map +7 -0
- package/dist/modules/billing/commands/topups.js +9 -2
- package/dist/modules/billing/commands/topups.js.map +2 -2
- package/dist/modules/billing/commands/transactions.js +2 -2
- package/dist/modules/billing/commands/transactions.js.map +2 -2
- package/dist/modules/billing/components/AccountForm.js +149 -42
- package/dist/modules/billing/components/AccountForm.js.map +2 -2
- package/dist/modules/billing/components/AccountInvoicesSection.js +64 -0
- package/dist/modules/billing/components/AccountInvoicesSection.js.map +7 -0
- package/dist/modules/billing/components/AccountPaymentsSection.js +43 -0
- package/dist/modules/billing/components/AccountPaymentsSection.js.map +7 -0
- package/dist/modules/billing/components/AccountPicker.js +56 -0
- package/dist/modules/billing/components/AccountPicker.js.map +7 -0
- package/dist/modules/billing/components/AllocatePaymentDialog.js +134 -0
- package/dist/modules/billing/components/AllocatePaymentDialog.js.map +7 -0
- package/dist/modules/billing/components/BillingDataTable.js +21 -0
- package/dist/modules/billing/components/BillingDataTable.js.map +7 -0
- package/dist/modules/billing/components/BillingSettingsLink.js +19 -0
- package/dist/modules/billing/components/BillingSettingsLink.js.map +7 -0
- package/dist/modules/billing/components/ChangeItemPriceDialog.js +158 -0
- package/dist/modules/billing/components/ChangeItemPriceDialog.js.map +7 -0
- package/dist/modules/billing/components/CreateMemoDialog.js +200 -0
- package/dist/modules/billing/components/CreateMemoDialog.js.map +7 -0
- package/dist/modules/billing/components/CustomerLabel.js +33 -0
- package/dist/modules/billing/components/CustomerLabel.js.map +7 -0
- package/dist/modules/billing/components/InvoicesTable.js +270 -0
- package/dist/modules/billing/components/InvoicesTable.js.map +7 -0
- package/dist/modules/billing/components/ItemForm.js +176 -44
- package/dist/modules/billing/components/ItemForm.js.map +2 -2
- package/dist/modules/billing/components/ItemPriceField.js +46 -0
- package/dist/modules/billing/components/ItemPriceField.js.map +7 -0
- package/dist/modules/billing/components/LineFormDialog.js +25 -2
- package/dist/modules/billing/components/LineFormDialog.js.map +2 -2
- package/dist/modules/billing/components/ManualPaymentDialog.js +122 -0
- package/dist/modules/billing/components/ManualPaymentDialog.js.map +7 -0
- package/dist/modules/billing/components/MoneyInput.js +14 -0
- package/dist/modules/billing/components/MoneyInput.js.map +7 -0
- package/dist/modules/billing/components/PaymentUploadDialog.js +115 -0
- package/dist/modules/billing/components/PaymentUploadDialog.js.map +7 -0
- package/dist/modules/billing/components/PaymentsTable.js +171 -0
- package/dist/modules/billing/components/PaymentsTable.js.map +7 -0
- package/dist/modules/billing/components/PostRunDraftsButton.js +105 -0
- package/dist/modules/billing/components/PostRunDraftsButton.js.map +7 -0
- package/dist/modules/billing/components/PrepaidPanel.js +14 -4
- package/dist/modules/billing/components/PrepaidPanel.js.map +2 -2
- package/dist/modules/billing/components/RunCyclesSection.js +209 -0
- package/dist/modules/billing/components/RunCyclesSection.js.map +7 -0
- package/dist/modules/billing/components/TestRunActions.js +127 -0
- package/dist/modules/billing/components/TestRunActions.js.map +7 -0
- package/dist/modules/billing/components/account/AccountItemsTab.js +157 -0
- package/dist/modules/billing/components/account/AccountItemsTab.js.map +7 -0
- package/dist/modules/billing/components/account/AccountRunBillingDialog.js +122 -0
- package/dist/modules/billing/components/account/AccountRunBillingDialog.js.map +7 -0
- package/dist/modules/billing/components/account/AccountRunsTab.js +152 -0
- package/dist/modules/billing/components/account/AccountRunsTab.js.map +7 -0
- package/dist/modules/billing/components/account/AccountStatementsTab.js +98 -0
- package/dist/modules/billing/components/account/AccountStatementsTab.js.map +7 -0
- package/dist/modules/billing/components/account/AccountSummaryCards.js +125 -0
- package/dist/modules/billing/components/account/AccountSummaryCards.js.map +7 -0
- package/dist/modules/billing/components/account/AccountUsageTab.js +91 -0
- package/dist/modules/billing/components/account/AccountUsageTab.js.map +7 -0
- package/dist/modules/billing/components/account/AccountView.js +73 -0
- package/dist/modules/billing/components/account/AccountView.js.map +7 -0
- package/dist/modules/billing/components/account/accountRow.js +39 -0
- package/dist/modules/billing/components/account/accountRow.js.map +7 -0
- package/dist/modules/billing/components/customerNames.js +42 -0
- package/dist/modules/billing/components/customerNames.js.map +7 -0
- package/dist/modules/billing/data/entities.js +560 -4
- package/dist/modules/billing/data/entities.js.map +2 -2
- package/dist/modules/billing/data/entityIds.js +10 -1
- package/dist/modules/billing/data/entityIds.js.map +2 -2
- package/dist/modules/billing/data/validators.js +269 -15
- package/dist/modules/billing/data/validators.js.map +2 -2
- package/dist/modules/billing/events.js +50 -0
- package/dist/modules/billing/events.js.map +2 -2
- package/dist/modules/billing/i18n/en.json +457 -21
- package/dist/modules/billing/i18n/pl.json +457 -21
- package/dist/modules/billing/index.js +6 -2
- package/dist/modules/billing/index.js.map +2 -2
- package/dist/modules/billing/lib/accountFinancials.js +58 -0
- package/dist/modules/billing/lib/accountFinancials.js.map +7 -0
- package/dist/modules/billing/lib/accountLock.js +14 -0
- package/dist/modules/billing/lib/accountLock.js.map +7 -0
- package/dist/modules/billing/lib/accountStatus.js +9 -7
- package/dist/modules/billing/lib/accountStatus.js.map +2 -2
- package/dist/modules/billing/lib/accountSummary.js +106 -0
- package/dist/modules/billing/lib/accountSummary.js.map +7 -0
- package/dist/modules/billing/lib/allocation.js +356 -0
- package/dist/modules/billing/lib/allocation.js.map +7 -0
- package/dist/modules/billing/lib/allocationEvents.js +22 -0
- package/dist/modules/billing/lib/allocationEvents.js.map +7 -0
- package/dist/modules/billing/lib/billPeriod.js +15 -1
- package/dist/modules/billing/lib/billPeriod.js.map +2 -2
- package/dist/modules/billing/lib/billRunEngine.js +237 -102
- package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
- package/dist/modules/billing/lib/billingSettings.js +116 -0
- package/dist/modules/billing/lib/billingSettings.js.map +7 -0
- package/dist/modules/billing/lib/commandRoute.js +81 -0
- package/dist/modules/billing/lib/commandRoute.js.map +7 -0
- package/dist/modules/billing/lib/customerLookup.js +97 -0
- package/dist/modules/billing/lib/customerLookup.js.map +7 -0
- package/dist/modules/billing/lib/dateOnly.js +13 -0
- package/dist/modules/billing/lib/dateOnly.js.map +7 -0
- package/dist/modules/billing/lib/documentParties.js +16 -0
- package/dist/modules/billing/lib/documentParties.js.map +7 -0
- package/dist/modules/billing/lib/documentTypes.js +27 -0
- package/dist/modules/billing/lib/documentTypes.js.map +7 -0
- package/dist/modules/billing/lib/dueDate.js +18 -0
- package/dist/modules/billing/lib/dueDate.js.map +7 -0
- package/dist/modules/billing/lib/iban.js +29 -0
- package/dist/modules/billing/lib/iban.js.map +7 -0
- package/dist/modules/billing/lib/invoiceDelivery.js +131 -0
- package/dist/modules/billing/lib/invoiceDelivery.js.map +7 -0
- package/dist/modules/billing/lib/invoiceLanguages.js +16 -0
- package/dist/modules/billing/lib/invoiceLanguages.js.map +7 -0
- package/dist/modules/billing/lib/invoiceWriter.js +107 -99
- package/dist/modules/billing/lib/invoiceWriter.js.map +2 -2
- package/dist/modules/billing/lib/itemBillingState.js +27 -0
- package/dist/modules/billing/lib/itemBillingState.js.map +7 -0
- package/dist/modules/billing/lib/itemPriceChange.js +18 -0
- package/dist/modules/billing/lib/itemPriceChange.js.map +7 -0
- package/dist/modules/billing/lib/itemSelector.js.map +2 -2
- package/dist/modules/billing/lib/lineAmounts.js +32 -0
- package/dist/modules/billing/lib/lineAmounts.js.map +7 -0
- package/dist/modules/billing/lib/memoGuards.js +25 -0
- package/dist/modules/billing/lib/memoGuards.js.map +7 -0
- package/dist/modules/billing/lib/navigation.js +48 -0
- package/dist/modules/billing/lib/navigation.js.map +7 -0
- package/dist/modules/billing/lib/numbering.js +120 -0
- package/dist/modules/billing/lib/numbering.js.map +7 -0
- package/dist/modules/billing/lib/overdue.js +36 -0
- package/dist/modules/billing/lib/overdue.js.map +7 -0
- package/dist/modules/billing/lib/paymentCsv.js +125 -0
- package/dist/modules/billing/lib/paymentCsv.js.map +7 -0
- package/dist/modules/billing/lib/paymentGateway.js +15 -0
- package/dist/modules/billing/lib/paymentGateway.js.map +7 -0
- package/dist/modules/billing/lib/paymentImport.js +164 -0
- package/dist/modules/billing/lib/paymentImport.js.map +7 -0
- package/dist/modules/billing/lib/paymentMatcher.js +36 -0
- package/dist/modules/billing/lib/paymentMatcher.js.map +7 -0
- package/dist/modules/billing/lib/pdf/attachmentStore.js +46 -0
- package/dist/modules/billing/lib/pdf/attachmentStore.js.map +7 -0
- package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js +7 -0
- package/dist/modules/billing/lib/pdf/fonts/embeddedFonts.js.map +7 -0
- package/dist/modules/billing/lib/pdf/invoiceModel.js +194 -0
- package/dist/modules/billing/lib/pdf/invoiceModel.js.map +7 -0
- package/dist/modules/billing/lib/pdf/invoicePdf.js +165 -0
- package/dist/modules/billing/lib/pdf/invoicePdf.js.map +7 -0
- package/dist/modules/billing/lib/pdf/invoiceTemplate.js +274 -0
- package/dist/modules/billing/lib/pdf/invoiceTemplate.js.map +7 -0
- package/dist/modules/billing/lib/prepaidConfig.js +17 -25
- package/dist/modules/billing/lib/prepaidConfig.js.map +2 -2
- package/dist/modules/billing/lib/prepaidConsume.js +26 -7
- package/dist/modules/billing/lib/prepaidConsume.js.map +2 -2
- package/dist/modules/billing/lib/prepaidPeriodClose.js +277 -158
- package/dist/modules/billing/lib/prepaidPeriodClose.js.map +2 -2
- package/dist/modules/billing/lib/prepaidTaxPoint.js +37 -0
- package/dist/modules/billing/lib/prepaidTaxPoint.js.map +7 -0
- package/dist/modules/billing/lib/seeds/demo.js +51 -3
- package/dist/modules/billing/lib/seeds/demo.js.map +2 -2
- package/dist/modules/billing/lib/seeds/paymentsSample.js +93 -0
- package/dist/modules/billing/lib/seeds/paymentsSample.js.map +7 -0
- package/dist/modules/billing/lib/seeds/sellerSettings.js +41 -0
- package/dist/modules/billing/lib/seeds/sellerSettings.js.map +7 -0
- package/dist/modules/billing/lib/servicePeriods.js +121 -0
- package/dist/modules/billing/lib/servicePeriods.js.map +7 -0
- package/dist/modules/billing/lib/testRunCleanup.js +24 -0
- package/dist/modules/billing/lib/testRunCleanup.js.map +7 -0
- package/dist/modules/billing/lib/topupCapture.js +2 -2
- package/dist/modules/billing/lib/topupCapture.js.map +2 -2
- package/dist/modules/billing/lib/topupReceipt.js +58 -79
- package/dist/modules/billing/lib/topupReceipt.js.map +2 -2
- package/dist/modules/billing/lib/usageRater.js.map +1 -1
- package/dist/modules/billing/lib/usageRunner.js +14 -6
- package/dist/modules/billing/lib/usageRunner.js.map +2 -2
- package/dist/modules/billing/lib/vat.js +39 -0
- package/dist/modules/billing/lib/vat.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js +15 -0
- package/dist/modules/billing/migrations/Migration20261114000000_billing_customer_kind.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js +25 -0
- package/dist/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js +115 -0
- package/dist/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js +47 -0
- package/dist/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js +89 -0
- package/dist/modules/billing/migrations/Migration20261117000000_billing_payments.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js +17 -0
- package/dist/modules/billing/migrations/Migration20261118000000_billing_memos.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js +27 -0
- package/dist/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.js.map +7 -0
- package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js +29 -0
- package/dist/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.js.map +7 -0
- package/dist/modules/billing/setup.js +7 -184
- package/dist/modules/billing/setup.js.map +2 -2
- package/dist/modules/billing/subscribers/credit-memo-auto-send.js +16 -0
- package/dist/modules/billing/subscribers/credit-memo-auto-send.js.map +7 -0
- package/dist/modules/billing/subscribers/debit-memo-apply-credit.js +16 -0
- package/dist/modules/billing/subscribers/debit-memo-apply-credit.js.map +7 -0
- package/dist/modules/billing/subscribers/debit-memo-auto-send.js +16 -0
- package/dist/modules/billing/subscribers/debit-memo-auto-send.js.map +7 -0
- package/dist/modules/billing/subscribers/invoice-apply-credit.js +34 -0
- package/dist/modules/billing/subscribers/invoice-apply-credit.js.map +7 -0
- package/dist/modules/billing/subscribers/invoice-auto-send.js +30 -0
- package/dist/modules/billing/subscribers/invoice-auto-send.js.map +7 -0
- package/dist/modules/billing/subscribers/payment-captured-to-paid.js +69 -33
- package/dist/modules/billing/subscribers/payment-captured-to-paid.js.map +2 -2
- package/dist/modules/billing/subscribers/prepaid-topup-captured.js +4 -6
- package/dist/modules/billing/subscribers/prepaid-topup-captured.js.map +2 -2
- package/generated/entities/billing_account/index.ts +3 -0
- package/generated/entities/billing_allocation/index.ts +18 -0
- package/generated/entities/billing_document_sequence/index.ts +11 -0
- package/generated/entities/billing_invoice/index.ts +38 -0
- package/generated/entities/billing_invoice_line/index.ts +24 -0
- package/generated/entities/billing_item/index.ts +4 -0
- package/generated/entities/billing_payment/index.ts +25 -0
- package/generated/entities/billing_payment_import/index.ts +14 -0
- package/generated/entities/billing_run/index.ts +0 -1
- package/generated/entities/billing_run_outcome/index.ts +1 -0
- package/generated/entities/billing_settings/index.ts +26 -0
- package/generated/entities/billing_statement/index.ts +1 -0
- package/generated/entities.ids.generated.ts +7 -0
- package/package.json +5 -1
- package/src/modules/billing/__integration__/TC-BILL-004.spec.ts +170 -0
- package/src/modules/billing/__integration__/TC-BILL-005.spec.ts +134 -0
- package/src/modules/billing/__integration__/TC-BILL-006.spec.ts +101 -0
- package/src/modules/billing/__integration__/TC-BILL-007.spec.ts +140 -0
- package/src/modules/billing/__integration__/TC-BILL-008.spec.ts +108 -0
- package/src/modules/billing/__integration__/TC-BILL-009.spec.ts +113 -0
- package/src/modules/billing/__integration__/TC-BILL-010.spec.ts +118 -0
- package/src/modules/billing/__integration__/TC-BILL-011.spec.ts +142 -0
- package/src/modules/billing/__integration__/TC-BILL-012.spec.ts +115 -0
- package/src/modules/billing/__integration__/TC-BILL-013.spec.ts +118 -0
- package/src/modules/billing/__integration__/sellerSettings.ts +24 -0
- package/src/modules/billing/__tests__/commands/invoices.test.ts +436 -354
- package/src/modules/billing/__tests__/commands/itemsBulk.test.ts +33 -0
- package/src/modules/billing/__tests__/commands/memos.test.ts +205 -0
- package/src/modules/billing/__tests__/commands/payments.test.ts +241 -0
- package/src/modules/billing/__tests__/commands/runsDelete.test.ts +146 -0
- package/src/modules/billing/__tests__/commands/runsPostDrafts.test.ts +106 -0
- package/src/modules/billing/__tests__/commands/topups.test.ts +11 -0
- package/src/modules/billing/__tests__/entityRegistry.test.ts +8 -1
- package/src/modules/billing/__tests__/helpers/prepaidMockEm.ts +28 -11
- package/src/modules/billing/__tests__/lib/accountFinancials.test.ts +53 -0
- package/src/modules/billing/__tests__/lib/accountSummary.test.ts +97 -0
- package/src/modules/billing/__tests__/lib/allocation.test.ts +319 -0
- package/src/modules/billing/__tests__/lib/billRunEngine.test.ts +228 -17
- package/src/modules/billing/__tests__/lib/billingSettings.test.ts +109 -0
- package/src/modules/billing/__tests__/lib/customerLookup.test.ts +156 -0
- package/src/modules/billing/__tests__/lib/dueDate.test.ts +35 -0
- package/src/modules/billing/__tests__/lib/iban.test.ts +38 -0
- package/src/modules/billing/__tests__/lib/invoiceDelivery.test.ts +189 -0
- package/src/modules/billing/__tests__/lib/invoiceWriter.test.ts +212 -10
- package/src/modules/billing/__tests__/lib/itemBillingState.test.ts +42 -0
- package/src/modules/billing/__tests__/lib/itemPriceChange.test.ts +165 -0
- package/src/modules/billing/__tests__/lib/lineAmounts.test.ts +86 -0
- package/src/modules/billing/__tests__/lib/navigation.test.ts +71 -0
- package/src/modules/billing/__tests__/lib/numbering.test.ts +91 -0
- package/src/modules/billing/__tests__/lib/overdue.test.ts +36 -0
- package/src/modules/billing/__tests__/lib/paymentCsv.test.ts +148 -0
- package/src/modules/billing/__tests__/lib/paymentImport.test.ts +113 -0
- package/src/modules/billing/__tests__/lib/paymentsSample.test.ts +73 -0
- package/src/modules/billing/__tests__/lib/pdf/invoicePdf.test.ts +199 -0
- package/src/modules/billing/__tests__/lib/pdf/invoicePdfService.test.ts +147 -0
- package/src/modules/billing/__tests__/lib/prepaidPeriodClose.test.ts +374 -98
- package/src/modules/billing/__tests__/lib/prepaidTaxPoint.test.ts +118 -0
- package/src/modules/billing/__tests__/lib/seedsDemo.test.ts +53 -2
- package/src/modules/billing/__tests__/lib/sellerSettings.test.ts +28 -0
- package/src/modules/billing/__tests__/lib/servicePeriods.test.ts +190 -0
- package/src/modules/billing/__tests__/lib/topupCapture.test.ts +6 -28
- package/src/modules/billing/__tests__/lib/topupReceipt.test.ts +21 -52
- package/src/modules/billing/__tests__/lib/vat.test.ts +51 -0
- package/src/modules/billing/__tests__/package-isolation.test.ts +22 -8
- package/src/modules/billing/__tests__/setup.test.ts +13 -4
- package/src/modules/billing/__tests__/validators.test.ts +193 -3
- package/src/modules/billing/acl.ts +12 -0
- package/src/modules/billing/api/accounts/[id]/summary/route.ts +62 -0
- package/src/modules/billing/api/accounts/route.ts +58 -4
- package/src/modules/billing/api/export/account/[id]/route.ts +13 -11
- package/src/modules/billing/api/invoices/[id]/memos/route.ts +63 -0
- package/src/modules/billing/api/invoices/[id]/pdf/route.ts +79 -0
- package/src/modules/billing/api/invoices/[id]/route.ts +57 -70
- package/src/modules/billing/api/invoices/[id]/send/route.ts +127 -0
- package/src/modules/billing/api/invoices/add-line/route.ts +2 -2
- package/src/modules/billing/api/invoices/delete-draft/route.ts +33 -0
- package/src/modules/billing/api/invoices/edit-line/route.ts +2 -2
- package/src/modules/billing/api/invoices/post/route.ts +7 -6
- package/src/modules/billing/api/invoices/remove-line/route.ts +2 -2
- package/src/modules/billing/api/invoices/route.ts +81 -86
- package/src/modules/billing/api/items/bulk/route.ts +2 -1
- package/src/modules/billing/api/items/change-price/route.ts +54 -0
- package/src/modules/billing/api/items/route.ts +23 -7
- package/src/modules/billing/api/payment-imports/[id]/route.ts +49 -0
- package/src/modules/billing/api/payment-imports/route.ts +57 -0
- package/src/modules/billing/api/payments/[id]/route.ts +63 -0
- package/src/modules/billing/api/payments/allocate/route.ts +35 -0
- package/src/modules/billing/api/payments/assign/route.ts +36 -0
- package/src/modules/billing/api/payments/convert-to-topup/route.ts +35 -0
- package/src/modules/billing/api/payments/ignore/route.ts +35 -0
- package/src/modules/billing/api/payments/import/route.ts +72 -0
- package/src/modules/billing/api/payments/restore/route.ts +35 -0
- package/src/modules/billing/api/payments/route.ts +147 -0
- package/src/modules/billing/api/payments/unallocate/route.ts +35 -0
- package/src/modules/billing/api/payments/unassign/route.ts +35 -0
- package/src/modules/billing/api/run-outcomes/route.ts +49 -6
- package/src/modules/billing/api/runs/[id]/post-drafts/route.ts +71 -0
- package/src/modules/billing/api/runs/[id]/route.ts +59 -0
- package/src/modules/billing/api/runs/retry-failed/route.ts +2 -1
- package/src/modules/billing/api/runs/route.ts +15 -7
- package/src/modules/billing/api/settings/number-preview/route.ts +46 -0
- package/src/modules/billing/api/settings/route.ts +116 -0
- package/src/modules/billing/api/statements/route.ts +5 -0
- package/src/modules/billing/api/test-invoices/wipe/route.ts +2 -2
- package/src/modules/billing/backend/billing/accounts/[id]/page.tsx +151 -372
- package/src/modules/billing/backend/billing/accounts/create/page.tsx +6 -0
- package/src/modules/billing/backend/billing/accounts/page.tsx +70 -4
- package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +444 -95
- package/src/modules/billing/backend/billing/invoices/page.tsx +8 -227
- package/src/modules/billing/backend/billing/items/[id]/page.tsx +75 -6
- package/src/modules/billing/backend/billing/items/create/page.tsx +13 -8
- package/src/modules/billing/backend/billing/items/page.tsx +18 -2
- package/src/modules/billing/backend/billing/payments/[id]/page.meta.ts +19 -0
- package/src/modules/billing/backend/billing/payments/[id]/page.tsx +415 -0
- package/src/modules/billing/backend/billing/payments/imports/[id]/page.meta.ts +19 -0
- package/src/modules/billing/backend/billing/payments/imports/[id]/page.tsx +106 -0
- package/src/modules/billing/backend/billing/payments/page.meta.ts +16 -0
- package/src/modules/billing/backend/billing/payments/page.tsx +56 -0
- package/src/modules/billing/backend/billing/runs/[id]/page.tsx +49 -18
- package/src/modules/billing/backend/billing/runs/page.tsx +59 -8
- package/src/modules/billing/backend/billing/statements/[id]/page.tsx +86 -5
- package/src/modules/billing/backend/billing/statements/page.tsx +1 -1
- package/src/modules/billing/backend/billing/topups/page.tsx +1 -1
- package/src/modules/billing/backend/billing/transactions/page.tsx +1 -1
- package/src/modules/billing/backend/billing/usage/create/page.tsx +3 -2
- package/src/modules/billing/backend/config/billing/page.meta.ts +21 -0
- package/src/modules/billing/backend/config/billing/page.tsx +549 -0
- package/src/modules/billing/cli.ts +31 -1
- package/src/modules/billing/commands/accounts.ts +6 -0
- package/src/modules/billing/commands/index.ts +3 -0
- package/src/modules/billing/commands/invoices.ts +364 -231
- package/src/modules/billing/commands/items.ts +143 -0
- package/src/modules/billing/commands/memos.ts +218 -0
- package/src/modules/billing/commands/payments.ts +428 -0
- package/src/modules/billing/commands/runs.ts +205 -3
- package/src/modules/billing/commands/settings.ts +77 -0
- package/src/modules/billing/commands/topups.ts +12 -3
- package/src/modules/billing/commands/transactions.ts +2 -2
- package/src/modules/billing/components/AccountForm.tsx +199 -53
- package/src/modules/billing/components/AccountInvoicesSection.tsx +75 -0
- package/src/modules/billing/components/AccountPaymentsSection.tsx +42 -0
- package/src/modules/billing/components/AccountPicker.tsx +70 -0
- package/src/modules/billing/components/AllocatePaymentDialog.tsx +167 -0
- package/src/modules/billing/components/BillingDataTable.tsx +34 -0
- package/src/modules/billing/components/BillingSettingsLink.tsx +25 -0
- package/src/modules/billing/components/ChangeItemPriceDialog.tsx +180 -0
- package/src/modules/billing/components/CreateMemoDialog.tsx +232 -0
- package/src/modules/billing/components/CustomerLabel.tsx +53 -0
- package/src/modules/billing/components/InvoicesTable.tsx +344 -0
- package/src/modules/billing/components/ItemForm.tsx +233 -44
- package/src/modules/billing/components/ItemPriceField.tsx +81 -0
- package/src/modules/billing/components/LineFormDialog.tsx +25 -1
- package/src/modules/billing/components/ManualPaymentDialog.tsx +140 -0
- package/src/modules/billing/components/MoneyInput.tsx +26 -0
- package/src/modules/billing/components/PaymentUploadDialog.tsx +148 -0
- package/src/modules/billing/components/PaymentsTable.tsx +213 -0
- package/src/modules/billing/components/PostRunDraftsButton.tsx +129 -0
- package/src/modules/billing/components/PrepaidPanel.tsx +19 -6
- package/src/modules/billing/components/RunCyclesSection.tsx +298 -0
- package/src/modules/billing/components/TestRunActions.tsx +152 -0
- package/src/modules/billing/components/account/AccountItemsTab.tsx +192 -0
- package/src/modules/billing/components/account/AccountRunBillingDialog.tsx +147 -0
- package/src/modules/billing/components/account/AccountRunsTab.tsx +201 -0
- package/src/modules/billing/components/account/AccountStatementsTab.tsx +120 -0
- package/src/modules/billing/components/account/AccountSummaryCards.tsx +153 -0
- package/src/modules/billing/components/account/AccountUsageTab.tsx +122 -0
- package/src/modules/billing/components/account/AccountView.tsx +101 -0
- package/src/modules/billing/components/account/accountRow.ts +70 -0
- package/src/modules/billing/components/customerNames.ts +62 -0
- package/src/modules/billing/data/entities.ts +654 -5
- package/src/modules/billing/data/entityIds.ts +9 -0
- package/src/modules/billing/data/validators.ts +388 -8
- package/src/modules/billing/events.ts +56 -3
- package/src/modules/billing/i18n/en.json +457 -21
- package/src/modules/billing/i18n/pl.json +457 -21
- package/src/modules/billing/index.ts +6 -2
- package/src/modules/billing/lib/accountFinancials.ts +84 -0
- package/src/modules/billing/lib/accountLock.ts +32 -0
- package/src/modules/billing/lib/accountStatus.ts +11 -9
- package/src/modules/billing/lib/accountSummary.ts +172 -0
- package/src/modules/billing/lib/allocation.ts +515 -0
- package/src/modules/billing/lib/allocationEvents.ts +26 -0
- package/src/modules/billing/lib/billPeriod.ts +19 -0
- package/src/modules/billing/lib/billRunEngine.ts +350 -150
- package/src/modules/billing/lib/billingSettings.ts +145 -0
- package/src/modules/billing/lib/commandRoute.ts +112 -0
- package/src/modules/billing/lib/customerLookup.ts +157 -0
- package/src/modules/billing/lib/dateOnly.ts +22 -0
- package/src/modules/billing/lib/documentParties.ts +23 -0
- package/src/modules/billing/lib/documentTypes.ts +37 -0
- package/src/modules/billing/lib/dueDate.ts +26 -0
- package/src/modules/billing/lib/iban.ts +36 -0
- package/src/modules/billing/lib/invoiceDelivery.ts +197 -0
- package/src/modules/billing/lib/invoiceLanguages.ts +34 -0
- package/src/modules/billing/lib/invoiceWriter.ts +162 -148
- package/src/modules/billing/lib/itemBillingState.ts +67 -0
- package/src/modules/billing/lib/itemPriceChange.ts +56 -0
- package/src/modules/billing/lib/itemSelector.ts +4 -1
- package/src/modules/billing/lib/lineAmounts.ts +61 -0
- package/src/modules/billing/lib/memoGuards.ts +41 -0
- package/src/modules/billing/lib/navigation.ts +64 -0
- package/src/modules/billing/lib/numbering.ts +174 -0
- package/src/modules/billing/lib/overdue.ts +60 -0
- package/src/modules/billing/lib/paymentCsv.ts +171 -0
- package/src/modules/billing/lib/paymentGateway.ts +52 -0
- package/src/modules/billing/lib/paymentImport.ts +228 -0
- package/src/modules/billing/lib/paymentMatcher.ts +88 -0
- package/src/modules/billing/lib/pdf/attachmentStore.ts +60 -0
- package/src/modules/billing/lib/pdf/fonts/NotoSans-Bold.ttf +0 -0
- package/src/modules/billing/lib/pdf/fonts/NotoSans-Regular.ttf +0 -0
- package/src/modules/billing/lib/pdf/fonts/OFL.txt +93 -0
- package/src/modules/billing/lib/pdf/fonts/embeddedFonts.ts +9 -0
- package/src/modules/billing/lib/pdf/invoiceModel.ts +328 -0
- package/src/modules/billing/lib/pdf/invoicePdf.ts +216 -0
- package/src/modules/billing/lib/pdf/invoiceTemplate.ts +338 -0
- package/src/modules/billing/lib/prepaidConfig.ts +27 -36
- package/src/modules/billing/lib/prepaidConsume.ts +29 -6
- package/src/modules/billing/lib/prepaidPeriodClose.ts +407 -239
- package/src/modules/billing/lib/prepaidTaxPoint.ts +76 -0
- package/src/modules/billing/lib/seeds/demo.ts +79 -6
- package/src/modules/billing/lib/seeds/paymentsSample.ts +136 -0
- package/src/modules/billing/lib/seeds/sellerSettings.ts +78 -0
- package/src/modules/billing/lib/servicePeriods.ts +226 -0
- package/src/modules/billing/lib/testRunCleanup.ts +42 -0
- package/src/modules/billing/lib/topupCapture.ts +3 -4
- package/src/modules/billing/lib/topupReceipt.ts +70 -103
- package/src/modules/billing/lib/usageRater.ts +1 -1
- package/src/modules/billing/lib/usageRunner.ts +17 -5
- package/src/modules/billing/lib/vat.ts +56 -0
- package/src/modules/billing/migrations/Migration20261114000000_billing_customer_kind.ts +25 -0
- package/src/modules/billing/migrations/Migration20261114100000_billing_cycle_semantics.ts +37 -0
- package/src/modules/billing/migrations/Migration20261115000000_billing_invoice_ledger.ts +132 -0
- package/src/modules/billing/migrations/Migration20261116000000_billing_invoice_documents.ts +58 -0
- package/src/modules/billing/migrations/Migration20261117000000_billing_payments.ts +97 -0
- package/src/modules/billing/migrations/Migration20261118000000_billing_memos.ts +21 -0
- package/src/modules/billing/migrations/Migration20261119000000_billing_operator_feedback.ts +39 -0
- package/src/modules/billing/migrations/Migration20261120000000_billing_allocation_scope_indexes.ts +35 -0
- package/src/modules/billing/setup.ts +11 -237
- package/src/modules/billing/subscribers/credit-memo-auto-send.ts +15 -0
- package/src/modules/billing/subscribers/debit-memo-apply-credit.ts +15 -0
- package/src/modules/billing/subscribers/debit-memo-auto-send.ts +15 -0
- package/src/modules/billing/subscribers/invoice-apply-credit.ts +39 -0
- package/src/modules/billing/subscribers/invoice-auto-send.ts +46 -0
- package/src/modules/billing/subscribers/payment-captured-to-paid.ts +84 -62
- package/src/modules/billing/subscribers/prepaid-topup-captured.ts +4 -8
- package/dist/modules/billing/lib/invoiceStatus.js +0 -38
- package/dist/modules/billing/lib/invoiceStatus.js.map +0 -7
- package/src/modules/billing/__tests__/lib/invoiceStatus.test.ts +0 -97
- package/src/modules/billing/lib/invoiceStatus.ts +0 -61
|
@@ -15,10 +15,10 @@ import { metadata } from '../index'
|
|
|
15
15
|
|
|
16
16
|
const PACKAGE_ROOT = path.resolve(__dirname, '..')
|
|
17
17
|
|
|
18
|
-
// Must mirror peerDependencies in packages/billing/package.json
|
|
19
|
-
// relatives + node builtins). Adding a prefix here without adding it to
|
|
20
|
-
//
|
|
21
|
-
//
|
|
18
|
+
// Must mirror peerDependencies + dependencies in packages/billing/package.json
|
|
19
|
+
// (plus relatives + node builtins). Adding a prefix here without adding it to
|
|
20
|
+
// package.json = silent runtime failure for consumers that don't have the dep
|
|
21
|
+
// transitively. `pdf-lib` / `@pdf-lib/` are regular dependencies (Spec B PDF).
|
|
22
22
|
const ALLOWED_IMPORT_PREFIXES = [
|
|
23
23
|
'./',
|
|
24
24
|
'../',
|
|
@@ -36,6 +36,8 @@ const ALLOWED_IMPORT_PREFIXES = [
|
|
|
36
36
|
'@dainamite/',
|
|
37
37
|
'node:',
|
|
38
38
|
'@tanstack/',
|
|
39
|
+
'pdf-lib',
|
|
40
|
+
'@pdf-lib/',
|
|
39
41
|
]
|
|
40
42
|
|
|
41
43
|
const NODE_BUILTINS = new Set([
|
|
@@ -128,10 +130,22 @@ describe('@dainamite/billing package isolation', () => {
|
|
|
128
130
|
}
|
|
129
131
|
})
|
|
130
132
|
|
|
131
|
-
it('
|
|
132
|
-
expect(metadata.requires).toEqual(
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
it('requires only platform modules (Spec 0: no core business modules; Spec B: attachments)', () => {
|
|
134
|
+
expect(metadata.requires).toEqual(['auth', 'directory', 'attachments'])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('never imports core sales / configs / dictionaries / payment_gateways (Spec 0)', () => {
|
|
138
|
+
const banned = new Set(['sales', 'configs', 'dictionaries', 'payment_gateways'])
|
|
139
|
+
const violations: string[] = []
|
|
140
|
+
for (const [file, specs] of importsByFile) {
|
|
141
|
+
for (const spec of specs) {
|
|
142
|
+
const subModule = extractCoreModuleSubpath(spec)
|
|
143
|
+
if (subModule && banned.has(subModule)) violations.push(` ${path.relative(PACKAGE_ROOT, file)} → ${spec}`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (violations.length > 0) {
|
|
147
|
+
throw new Error(`Billing must not depend on core business modules:\n${violations.join('\n')}`)
|
|
148
|
+
}
|
|
135
149
|
})
|
|
136
150
|
|
|
137
151
|
it('does not import from sibling app modules', () => {
|
|
@@ -16,9 +16,17 @@ import setup from '../setup'
|
|
|
16
16
|
const featureIds = new Set<string>(features.map((f) => f.id))
|
|
17
17
|
|
|
18
18
|
describe('billing setup — contract', () => {
|
|
19
|
-
it('exports
|
|
20
|
-
expect(typeof setup.onTenantCreated).toBe('function')
|
|
19
|
+
it('exports a seedDefaults hook (billing settings) and no core-config tenant hook', () => {
|
|
21
20
|
expect(typeof setup.seedDefaults).toBe('function')
|
|
21
|
+
expect(setup.onTenantCreated).toBeUndefined()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('seedDefaults warms up the organization billing settings row', async () => {
|
|
25
|
+
const row = { id: 's-1' }
|
|
26
|
+
const em = { findOne: jest.fn(async () => row), execute: jest.fn() }
|
|
27
|
+
await setup.seedDefaults!({ em, tenantId: 't-1', organizationId: 'o-1' } as never)
|
|
28
|
+
expect(em.findOne).toHaveBeenCalledTimes(1)
|
|
29
|
+
expect(em.execute).not.toHaveBeenCalled()
|
|
22
30
|
})
|
|
23
31
|
|
|
24
32
|
it('does not declare seedExamples (billing ships no demo data)', () => {
|
|
@@ -77,8 +85,8 @@ describe('billing setup — contract', () => {
|
|
|
77
85
|
})
|
|
78
86
|
|
|
79
87
|
describe('billing acl — surface', () => {
|
|
80
|
-
it('exports exactly
|
|
81
|
-
expect(features).toHaveLength(
|
|
88
|
+
it('exports exactly 27 features (12 Phase 0 + 7 SPEC-002 prepaid + 2 Spec 0 settings + 1 Spec B send + 3 Spec C payments + 1 Spec D memos + 1 Spec F run delete)', () => {
|
|
89
|
+
expect(features).toHaveLength(27)
|
|
82
90
|
})
|
|
83
91
|
|
|
84
92
|
it('all feature IDs are prefixed `billing.` (no leaking module boundary)', () => {
|
|
@@ -98,6 +106,7 @@ describe('billing acl — surface', () => {
|
|
|
98
106
|
'billing.run.trigger',
|
|
99
107
|
'billing.run.dry_run',
|
|
100
108
|
'billing.run.view',
|
|
109
|
+
'billing.run.delete',
|
|
101
110
|
'billing.invoice.post',
|
|
102
111
|
'billing.invoice.edit_draft',
|
|
103
112
|
'billing.invoice.view',
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
rateJsonUsageVolume,
|
|
21
21
|
validateRateJson,
|
|
22
22
|
} from '../data/validators'
|
|
23
|
+
import { DEFAULT_INVOICE_LANGUAGE, SUPPORTED_INVOICE_LANGUAGES } from '../lib/invoiceLanguages'
|
|
23
24
|
|
|
24
25
|
describe('rate_json — one_time', () => {
|
|
25
26
|
it('accepts a positive amount', () => {
|
|
@@ -293,9 +294,6 @@ describe('exported enums', () => {
|
|
|
293
294
|
])
|
|
294
295
|
})
|
|
295
296
|
|
|
296
|
-
it('INVOICE_STATUSES matches the dictionary entries seeded by setup', () => {
|
|
297
|
-
expect([...INVOICE_STATUSES]).toEqual(['draft', 'posted', 'paid', 'void'])
|
|
298
|
-
})
|
|
299
297
|
})
|
|
300
298
|
|
|
301
299
|
// ─── Phase 1 — CRUD payload schemas ────────────────────────────────
|
|
@@ -304,6 +302,11 @@ import {
|
|
|
304
302
|
billingAccountCreateSchema,
|
|
305
303
|
billingAccountListQuerySchema,
|
|
306
304
|
billingAccountUpdateSchema,
|
|
305
|
+
billingItemBulkCreateSchema,
|
|
306
|
+
billingRunTriggerSchema,
|
|
307
|
+
CHARGE_TIMING_RECURRING_ONLY_MESSAGE,
|
|
308
|
+
billingInvoiceListQuerySchema,
|
|
309
|
+
billingSettingsUpdateSchema,
|
|
307
310
|
billingItemCreateSchema,
|
|
308
311
|
billingItemListQuerySchema,
|
|
309
312
|
billingItemUpdateSchema,
|
|
@@ -367,6 +370,45 @@ describe('billingAccountCreateSchema', () => {
|
|
|
367
370
|
expect(() => billingAccountCreateSchema.parse(withoutTenant)).toThrow()
|
|
368
371
|
void tenantId
|
|
369
372
|
})
|
|
373
|
+
// Spec E — B2C accounts + invoice-language picklist.
|
|
374
|
+
it('defaults customerKind to company', () => {
|
|
375
|
+
expect(billingAccountCreateSchema.parse(validAccountCreate).customerKind).toBe('company')
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
it('accepts customerKind person', () => {
|
|
379
|
+
const parsed = billingAccountCreateSchema.parse({ ...validAccountCreate, customerKind: 'person' })
|
|
380
|
+
expect(parsed.customerKind).toBe('person')
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
it('rejects an unknown customerKind', () => {
|
|
384
|
+
expect(() =>
|
|
385
|
+
billingAccountCreateSchema.parse({ ...validAccountCreate, customerKind: 'household' }),
|
|
386
|
+
).toThrow()
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('accepts every template-supported invoice language, case/space tolerant', () => {
|
|
390
|
+
for (const { code } of SUPPORTED_INVOICE_LANGUAGES) {
|
|
391
|
+
const parsed = billingAccountCreateSchema.parse({
|
|
392
|
+
...validAccountCreate,
|
|
393
|
+
invoiceLanguage: ` ${code.toUpperCase()} `,
|
|
394
|
+
})
|
|
395
|
+
expect(parsed.invoiceLanguage).toBe(code)
|
|
396
|
+
}
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
it('rejects an invoice language the template does not support', () => {
|
|
400
|
+
expect(() =>
|
|
401
|
+
billingAccountCreateSchema.parse({ ...validAccountCreate, invoiceLanguage: 'de' }),
|
|
402
|
+
).toThrow()
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
it('defaults invoiceLanguage when omitted', () => {
|
|
406
|
+
const { invoiceLanguage, ...withoutLanguage } = validAccountCreate
|
|
407
|
+
void invoiceLanguage
|
|
408
|
+
expect(billingAccountCreateSchema.parse(withoutLanguage).invoiceLanguage).toBe(
|
|
409
|
+
DEFAULT_INVOICE_LANGUAGE,
|
|
410
|
+
)
|
|
411
|
+
})
|
|
370
412
|
})
|
|
371
413
|
|
|
372
414
|
describe('billingAccountUpdateSchema', () => {
|
|
@@ -397,6 +439,16 @@ describe('billingAccountUpdateSchema', () => {
|
|
|
397
439
|
it('does not declare customerId (immutable per spec)', () => {
|
|
398
440
|
expect('customerId' in billingAccountUpdateSchema.shape).toBe(false)
|
|
399
441
|
})
|
|
442
|
+
|
|
443
|
+
it('does not declare customerKind (immutable per spec)', () => {
|
|
444
|
+
expect('customerKind' in billingAccountUpdateSchema.shape).toBe(false)
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
it('validates invoiceLanguage against supported languages', () => {
|
|
448
|
+
const base = { tenantId: TENANT, organizationId: ORG, id: ACCOUNT }
|
|
449
|
+
expect(billingAccountUpdateSchema.parse({ ...base, invoiceLanguage: 'PL' }).invoiceLanguage).toBe('pl')
|
|
450
|
+
expect(() => billingAccountUpdateSchema.parse({ ...base, invoiceLanguage: 'fr' })).toThrow()
|
|
451
|
+
})
|
|
400
452
|
})
|
|
401
453
|
|
|
402
454
|
describe('billingAccountListQuerySchema', () => {
|
|
@@ -415,6 +467,11 @@ describe('billingAccountListQuerySchema', () => {
|
|
|
415
467
|
expect(parsed.page).toBe(1)
|
|
416
468
|
expect(parsed.pageSize).toBe(50)
|
|
417
469
|
})
|
|
470
|
+
|
|
471
|
+
it('accepts a customerKind filter and rejects unknown kinds', () => {
|
|
472
|
+
expect(billingAccountListQuerySchema.parse({ customerKind: 'person' }).customerKind).toBe('person')
|
|
473
|
+
expect(() => billingAccountListQuerySchema.parse({ customerKind: 'robot' })).toThrow()
|
|
474
|
+
})
|
|
418
475
|
})
|
|
419
476
|
|
|
420
477
|
const validItemRecurring = {
|
|
@@ -592,3 +649,136 @@ describe('billingUsageListQuerySchema', () => {
|
|
|
592
649
|
expect(() => billingUsageListQuerySchema.parse({ pageSize: '101' })).toThrow()
|
|
593
650
|
})
|
|
594
651
|
})
|
|
652
|
+
|
|
653
|
+
// ─── Spec A — charge timing, proration, no catch-up flag ─────────
|
|
654
|
+
|
|
655
|
+
describe('Spec A validators', () => {
|
|
656
|
+
const recurring = {
|
|
657
|
+
tenantId: TENANT,
|
|
658
|
+
organizationId: ORG,
|
|
659
|
+
billAccountId: ACCOUNT,
|
|
660
|
+
type: 'recurring' as const,
|
|
661
|
+
billStartDate: '2026-11-14',
|
|
662
|
+
description: 'Internet 300',
|
|
663
|
+
rateJson: { unit_price: 79 },
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
it('accepts chargeTiming / prorate on recurring items and leaves defaults to the command', () => {
|
|
667
|
+
expect(billingItemCreateSchema.parse(recurring)).not.toHaveProperty('chargeTiming')
|
|
668
|
+
const parsed = billingItemCreateSchema.parse({ ...recurring, chargeTiming: 'in_arrears', prorate: false })
|
|
669
|
+
expect(parsed).toMatchObject({ chargeTiming: 'in_arrears', prorate: false })
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
it('rejects an unknown charge timing', () => {
|
|
673
|
+
expect(() => billingItemCreateSchema.parse({ ...recurring, chargeTiming: 'whenever' })).toThrow()
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
it('rejects non-default timing / proration on non-recurring items', () => {
|
|
677
|
+
const oneTime = { ...recurring, type: 'one_time' as const, rateJson: { amount: 10 } }
|
|
678
|
+
expect(billingItemCreateSchema.parse({ ...oneTime, chargeTiming: 'in_advance', prorate: true })).toBeTruthy()
|
|
679
|
+
expect(() => billingItemCreateSchema.parse({ ...oneTime, chargeTiming: 'in_arrears' })).toThrow(
|
|
680
|
+
CHARGE_TIMING_RECURRING_ONLY_MESSAGE,
|
|
681
|
+
)
|
|
682
|
+
expect(() => billingItemCreateSchema.parse({ ...oneTime, prorate: false })).toThrow(
|
|
683
|
+
CHARGE_TIMING_RECURRING_ONLY_MESSAGE,
|
|
684
|
+
)
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
it('applies the same rule to bulk entries and allows both on update', () => {
|
|
688
|
+
const { tenantId, organizationId, ...entry } = recurring
|
|
689
|
+
expect(() =>
|
|
690
|
+
billingItemBulkCreateSchema.parse({
|
|
691
|
+
tenantId,
|
|
692
|
+
organizationId,
|
|
693
|
+
items: [{ ...entry, type: 'usage', uomCode: 'gb', rateJson: { unit_price: 1 }, prorate: false }],
|
|
694
|
+
}),
|
|
695
|
+
).toThrow()
|
|
696
|
+
expect(
|
|
697
|
+
billingItemUpdateSchema.parse({ tenantId, organizationId, id: ACCOUNT, chargeTiming: 'in_arrears', prorate: false }),
|
|
698
|
+
).toMatchObject({ chargeTiming: 'in_arrears', prorate: false })
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
it('strips a legacy catchUp flag from the run trigger', () => {
|
|
702
|
+
const parsed = billingRunTriggerSchema.parse({ tenantId: TENANT, organizationId: ORG, catchUp: true })
|
|
703
|
+
expect(parsed).not.toHaveProperty('catchUp')
|
|
704
|
+
expect(parsed.mode).toBe('real')
|
|
705
|
+
})
|
|
706
|
+
})
|
|
707
|
+
|
|
708
|
+
// ─── Spec 0 — ledger validators ──────────────────────────────────
|
|
709
|
+
|
|
710
|
+
describe('Spec 0 validators', () => {
|
|
711
|
+
it('invoice statuses drop void and add partially_paid / credited', () => {
|
|
712
|
+
expect(INVOICE_STATUSES).toEqual(['draft', 'posted', 'partially_paid', 'paid', 'credited'])
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
it('parses testRun literally (false stays false)', () => {
|
|
716
|
+
expect(billingInvoiceListQuerySchema.parse({ testRun: 'false' }).testRun).toBe(false)
|
|
717
|
+
expect(billingInvoiceListQuerySchema.parse({ testRun: 'true' }).testRun).toBe(true)
|
|
718
|
+
expect(billingInvoiceListQuerySchema.parse({}).testRun).toBeUndefined()
|
|
719
|
+
expect(() => billingInvoiceListQuerySchema.parse({ status: 'void' })).toThrow()
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
it('validates number masks on settings update', () => {
|
|
723
|
+
const base = { tenantId: TENANT, organizationId: ORG }
|
|
724
|
+
expect(
|
|
725
|
+
billingSettingsUpdateSchema.parse({ ...base, numberMasks: { invoice: 'FV/{YYYY}/{SEQUENCE_NUM_YEAR:5}' } }).numberMasks,
|
|
726
|
+
).toEqual({ invoice: 'FV/{YYYY}/{SEQUENCE_NUM_YEAR:5}' })
|
|
727
|
+
expect(() => billingSettingsUpdateSchema.parse({ ...base, numberMasks: { invoice: 'FV/{SEQUENCE_NUM_MONTH}' } })).toThrow(
|
|
728
|
+
'mask_month_scope_needs_year_and_month',
|
|
729
|
+
)
|
|
730
|
+
expect(() => billingSettingsUpdateSchema.parse({ ...base, numberMasks: { bogus: 'X{SEQUENCE_NUM}' } })).toThrow()
|
|
731
|
+
expect(() => billingSettingsUpdateSchema.parse({ ...base, topupTaxRate: 120 })).toThrow()
|
|
732
|
+
})
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
describe('Spec B validators', () => {
|
|
736
|
+
const base = { tenantId: TENANT, organizationId: ORG }
|
|
737
|
+
const item = {
|
|
738
|
+
...base,
|
|
739
|
+
billAccountId: ACCOUNT,
|
|
740
|
+
type: 'recurring' as const,
|
|
741
|
+
billStartDate: '2026-11-14',
|
|
742
|
+
description: 'Internet 300',
|
|
743
|
+
rateJson: { unit_price: 79 },
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
it('item taxRate is optional on create (the command fills the default) and bounded 0–100', () => {
|
|
747
|
+
expect(billingItemCreateSchema.parse(item)).not.toHaveProperty('taxRate')
|
|
748
|
+
expect(billingItemCreateSchema.parse({ ...item, taxRate: '8' }).taxRate).toBe(8)
|
|
749
|
+
expect(() => billingItemCreateSchema.parse({ ...item, taxRate: -1 })).toThrow()
|
|
750
|
+
expect(() => billingItemCreateSchema.parse({ ...item, taxRate: 101 })).toThrow()
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
it('normalizes and checksum-validates the seller IBAN; empty clears it', () => {
|
|
754
|
+
expect(billingSettingsUpdateSchema.parse({ ...base, iban: 'pl61 1090 1014 0000 0712 1981 2874' }).iban).toBe(
|
|
755
|
+
'PL61109010140000071219812874',
|
|
756
|
+
)
|
|
757
|
+
expect(billingSettingsUpdateSchema.parse({ ...base, iban: '' }).iban).toBeNull()
|
|
758
|
+
expect(() => billingSettingsUpdateSchema.parse({ ...base, iban: 'PL61109010140000071219812875' })).toThrow(
|
|
759
|
+
'billing.settings.error.iban_invalid',
|
|
760
|
+
)
|
|
761
|
+
expect(() => billingSettingsUpdateSchema.parse({ ...base, bic: 'XX' })).toThrow('billing.settings.error.bic_invalid')
|
|
762
|
+
})
|
|
763
|
+
|
|
764
|
+
it('validates seller address, terms and default rate', () => {
|
|
765
|
+
const parsed = billingSettingsUpdateSchema.parse({
|
|
766
|
+
...base,
|
|
767
|
+
legalName: ' Seller ',
|
|
768
|
+
taxId: '',
|
|
769
|
+
sellerAddress: { line1: 'ul. A 1', city: 'Kraków', postal_code: '30-001', country: 'PL' },
|
|
770
|
+
defaultPaymentTermsDays: '30',
|
|
771
|
+
defaultTaxRate: 23,
|
|
772
|
+
})
|
|
773
|
+
expect(parsed).toMatchObject({ legalName: 'Seller', taxId: null, defaultPaymentTermsDays: 30, defaultTaxRate: 23 })
|
|
774
|
+
expect(() => billingSettingsUpdateSchema.parse({ ...base, sellerAddress: { line1: 'x' } })).toThrow()
|
|
775
|
+
expect(() => billingSettingsUpdateSchema.parse({ ...base, defaultPaymentTermsDays: 400 })).toThrow()
|
|
776
|
+
})
|
|
777
|
+
|
|
778
|
+
it('accepts overdue / due-date filters on the invoice list', () => {
|
|
779
|
+
const parsed = billingInvoiceListQuerySchema.parse({ overdue: 'true', dueFrom: '2026-11-01', dueTo: '2026-11-30' })
|
|
780
|
+
expect(parsed).toMatchObject({ overdue: true, dueFrom: '2026-11-01', dueTo: '2026-11-30' })
|
|
781
|
+
expect(() => billingInvoiceListQuerySchema.parse({ dueFrom: '01.11.2026' })).toThrow()
|
|
782
|
+
})
|
|
783
|
+
})
|
|
784
|
+
|
|
@@ -8,9 +8,18 @@ export const features = [
|
|
|
8
8
|
{ id: 'billing.run.trigger', title: 'Trigger Bill Run', module: 'billing' },
|
|
9
9
|
{ id: 'billing.run.dry_run', title: 'Trigger Bill Run (dry-run)', module: 'billing' },
|
|
10
10
|
{ id: 'billing.run.view', title: 'View Bill Run History', module: 'billing' },
|
|
11
|
+
// Spec F — delete test / dry runs (real runs are never deleted)
|
|
12
|
+
{ id: 'billing.run.delete', title: 'Delete Test / Dry Bill Runs', module: 'billing' },
|
|
11
13
|
{ id: 'billing.invoice.post', title: 'Post Draft Invoice', module: 'billing' },
|
|
12
14
|
{ id: 'billing.invoice.edit_draft', title: 'Edit Draft Invoice', module: 'billing' },
|
|
13
15
|
{ id: 'billing.invoice.view', title: 'View Billing Invoices', module: 'billing' },
|
|
16
|
+
{ id: 'billing.invoice.send', title: 'Email Billing Invoices', module: 'billing' },
|
|
17
|
+
// Spec D — corrective documents
|
|
18
|
+
{ id: 'billing.invoice.correct', title: 'Create Credit / Debit Memos', module: 'billing' },
|
|
19
|
+
// Spec C — payments
|
|
20
|
+
{ id: 'billing.payment.view', title: 'View Billing Payments', module: 'billing' },
|
|
21
|
+
{ id: 'billing.payment.manage', title: 'Manage Billing Payments (assign, allocate, ignore, convert)', module: 'billing' },
|
|
22
|
+
{ id: 'billing.payment.import', title: 'Import Bank Payments (CSV)', module: 'billing' },
|
|
14
23
|
// ─── Prepaid balance billing (SPEC-002) ──────────────────────
|
|
15
24
|
{ id: 'billing.balance.view', title: 'View Balance & Transactions', module: 'billing' },
|
|
16
25
|
{ id: 'billing.balance.adjust', title: 'Adjust Balance (manual credit/debit)', module: 'billing' },
|
|
@@ -19,6 +28,9 @@ export const features = [
|
|
|
19
28
|
{ id: 'billing.statement.view', title: 'View Consumption Statements', module: 'billing' },
|
|
20
29
|
{ id: 'billing.statement.generate', title: 'Generate Consumption Statement', module: 'billing' },
|
|
21
30
|
{ id: 'billing.credit.view', title: 'View Account Credit Status', module: 'billing' },
|
|
31
|
+
// ─── Billing settings (Spec 0) ───────────────────────────────
|
|
32
|
+
{ id: 'billing.settings.view', title: 'View Billing Settings', module: 'billing' },
|
|
33
|
+
{ id: 'billing.settings.manage', title: 'Manage Billing Settings', module: 'billing' },
|
|
22
34
|
] as const
|
|
23
35
|
|
|
24
36
|
export default features
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
5
|
+
import { BillingAccount } from '../../../../data/entities'
|
|
6
|
+
import { getAccountWorkspaceSummary } from '../../../../lib/accountSummary'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `GET /api/billing/accounts/[id]/summary` — what the customer owes (Spec B
|
|
10
|
+
* AR summary; Spec E extends the same endpoint for the account workspace).
|
|
11
|
+
* One aggregate query; see `lib/accountSummary.ts`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const metadata = {
|
|
15
|
+
GET: { requireAuth: true, requireFeatures: ['billing.account.view'] },
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
19
|
+
try {
|
|
20
|
+
const container = await createRequestContainer()
|
|
21
|
+
const auth = await getAuthFromRequest(req)
|
|
22
|
+
if (!auth || !auth.tenantId) {
|
|
23
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
24
|
+
}
|
|
25
|
+
const organizationId = (auth as { orgId?: string }).orgId ?? null
|
|
26
|
+
if (!organizationId) {
|
|
27
|
+
return NextResponse.json({ error: 'Organization scope required' }, { status: 403 })
|
|
28
|
+
}
|
|
29
|
+
const { id: accountId } = await params
|
|
30
|
+
const em = (container.resolve('em') as EntityManager).fork()
|
|
31
|
+
const account = await em.findOne(BillingAccount, {
|
|
32
|
+
id: accountId,
|
|
33
|
+
tenantId: auth.tenantId,
|
|
34
|
+
organizationId,
|
|
35
|
+
deletedAt: null,
|
|
36
|
+
})
|
|
37
|
+
if (!account) {
|
|
38
|
+
return NextResponse.json({ error: 'Account not found' }, { status: 404 })
|
|
39
|
+
}
|
|
40
|
+
return NextResponse.json(await getAccountWorkspaceSummary(em, account))
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.error('[billing/accounts/summary.GET]', err)
|
|
43
|
+
return NextResponse.json({ error: 'Internal error' }, { status: 500 })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const openApi = {
|
|
48
|
+
methods: {
|
|
49
|
+
GET: {
|
|
50
|
+
tags: ['Billing'],
|
|
51
|
+
summary: 'Get a billing account receivables summary',
|
|
52
|
+
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string', format: 'uuid' } }],
|
|
53
|
+
description:
|
|
54
|
+
'Returns `{ bill_account_id, currency_code, open_amount, overdue_amount, overdue_count, ' +
|
|
55
|
+
'credit_amount }` over posted / partially paid invoices and debit memos (test runs excluded). ' +
|
|
56
|
+
'Overdue = past due date with outstanding > 0. `credit_amount` = unallocated payments + open credit memos. ' +
|
|
57
|
+
'Spec F (account workspace) adds `billing_mode`, `bill_cycle`, `next_bill_date`, `last_invoice` ' +
|
|
58
|
+
'(`{ id, number, issue_date, total_gross }` or null) and, for prepaid accounts, `prepaid { balance, ' +
|
|
59
|
+
'balance_status, credit_limit, last_statement }` (null for postpaid).',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
}
|
|
@@ -2,7 +2,13 @@ import { z } from 'zod'
|
|
|
2
2
|
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
3
|
import { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'
|
|
4
4
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
5
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
6
|
+
import type { AwilixContainer } from 'awilix'
|
|
5
7
|
import { BillingAccount } from '../../data/entities'
|
|
8
|
+
import { fetchAccountFinancials, fetchOverdueAccountIds, financialsOrZero } from '../../lib/accountFinancials'
|
|
9
|
+
|
|
10
|
+
/** A uuid no row has — narrows an `$in` filter to nothing. */
|
|
11
|
+
const NO_MATCH_ID = '00000000-0000-4000-8000-000000000000'
|
|
6
12
|
import { billingEntityIds } from '../../data/entityIds'
|
|
7
13
|
import {
|
|
8
14
|
billingAccountCreateSchema,
|
|
@@ -33,6 +39,7 @@ export const metadata = routeMetadata
|
|
|
33
39
|
const billingAccountListItemSchema = z.object({
|
|
34
40
|
id: z.string().uuid(),
|
|
35
41
|
customer_id: z.string(),
|
|
42
|
+
customer_kind: z.enum(['company', 'person']),
|
|
36
43
|
name: z.string(),
|
|
37
44
|
currency_code: z.string(),
|
|
38
45
|
bill_cycle: z.string(),
|
|
@@ -45,6 +52,11 @@ const billingAccountListItemSchema = z.object({
|
|
|
45
52
|
last_bill_date: z.string().nullable(),
|
|
46
53
|
billing_mode: z.string(),
|
|
47
54
|
credit_limit: z.string(),
|
|
55
|
+
payment_terms_days: z.number().int().nullable(),
|
|
56
|
+
auto_send_invoice: z.boolean(),
|
|
57
|
+
/** Spec F (F8): Σ outstanding of open receivables and its overdue subset (batched per page). */
|
|
58
|
+
balance_due: z.string(),
|
|
59
|
+
overdue_amount: z.string(),
|
|
48
60
|
is_active: z.boolean(),
|
|
49
61
|
organization_id: z.string().uuid(),
|
|
50
62
|
tenant_id: z.string().uuid(),
|
|
@@ -71,6 +83,7 @@ const crud = makeCrudRoute({
|
|
|
71
83
|
fields: [
|
|
72
84
|
'id',
|
|
73
85
|
'customer_id',
|
|
86
|
+
'customer_kind',
|
|
74
87
|
'name',
|
|
75
88
|
'currency_code',
|
|
76
89
|
'bill_cycle',
|
|
@@ -83,6 +96,8 @@ const crud = makeCrudRoute({
|
|
|
83
96
|
'last_bill_date',
|
|
84
97
|
'billing_mode',
|
|
85
98
|
'credit_limit',
|
|
99
|
+
'payment_terms_days',
|
|
100
|
+
'auto_send_invoice',
|
|
86
101
|
'is_active',
|
|
87
102
|
'organization_id',
|
|
88
103
|
'tenant_id',
|
|
@@ -95,16 +110,28 @@ const crud = makeCrudRoute({
|
|
|
95
110
|
updatedAt: 'updated_at',
|
|
96
111
|
nextBillDate: 'next_bill_date',
|
|
97
112
|
},
|
|
98
|
-
buildFilters: async (query) => {
|
|
113
|
+
buildFilters: async (query, ctx) => {
|
|
99
114
|
const filters: Record<string, unknown> = {}
|
|
115
|
+
// Spec F (F8): accounts with an overdue receivable — one indexed
|
|
116
|
+
// DISTINCT over billing_invoices, intersected with any `id` filter.
|
|
117
|
+
if (query.hasOverdue === true && ctx.auth?.tenantId) {
|
|
118
|
+
const organizationIds = ctx.organizationIds ?? (ctx.selectedOrganizationId ? [ctx.selectedOrganizationId] : [])
|
|
119
|
+
const em = ctx.container.resolve<EntityManager>('em')
|
|
120
|
+
const overdueIds = await fetchOverdueAccountIds(em, { tenantId: ctx.auth.tenantId, organizationIds })
|
|
121
|
+
filters.id = { $in: overdueIds.length > 0 ? overdueIds : [NO_MATCH_ID] }
|
|
122
|
+
}
|
|
100
123
|
// `?id=<uuid>` narrows the list to one row — the detail page
|
|
101
124
|
// reads a single account through this filter.
|
|
102
125
|
if (typeof query.id === 'string' && query.id) {
|
|
103
|
-
filters.id
|
|
126
|
+
const current = filters.id as { $in?: string[] } | undefined
|
|
127
|
+
filters.id = current?.$in ? { $in: current.$in.includes(query.id) ? [query.id] : [NO_MATCH_ID] } : { $eq: query.id }
|
|
104
128
|
}
|
|
105
129
|
if (typeof query.customerId === 'string' && query.customerId) {
|
|
106
130
|
filters.customer_id = { $eq: query.customerId }
|
|
107
131
|
}
|
|
132
|
+
if (typeof query.customerKind === 'string' && query.customerKind) {
|
|
133
|
+
filters.customer_kind = { $eq: query.customerKind }
|
|
134
|
+
}
|
|
108
135
|
if (typeof query.currencyCode === 'string' && query.currencyCode) {
|
|
109
136
|
filters.currency_code = { $eq: query.currencyCode.toUpperCase() }
|
|
110
137
|
}
|
|
@@ -124,6 +151,30 @@ const crud = makeCrudRoute({
|
|
|
124
151
|
return filters
|
|
125
152
|
},
|
|
126
153
|
},
|
|
154
|
+
hooks: {
|
|
155
|
+
// Spec F (F8): balance due / overdue per row — one GROUP BY for the page.
|
|
156
|
+
afterList: async (payload: unknown, ctx: unknown) => {
|
|
157
|
+
const items = ((payload as { items?: unknown[] } | null)?.items ?? []) as Array<Record<string, unknown>>
|
|
158
|
+
if (items.length === 0) return
|
|
159
|
+
const first = items[0]
|
|
160
|
+
const tenantId = typeof first.tenant_id === 'string' ? first.tenant_id : null
|
|
161
|
+
const organizationId = typeof first.organization_id === 'string' ? first.organization_id : null
|
|
162
|
+
const ids = items.map((row) => row.id).filter((id): id is string => typeof id === 'string')
|
|
163
|
+
const financials =
|
|
164
|
+
tenantId && organizationId
|
|
165
|
+
? await fetchAccountFinancials(
|
|
166
|
+
(ctx as { container: AwilixContainer }).container.resolve<EntityManager>('em'),
|
|
167
|
+
{ tenantId, organizationId },
|
|
168
|
+
ids,
|
|
169
|
+
)
|
|
170
|
+
: new Map()
|
|
171
|
+
for (const row of items) {
|
|
172
|
+
const values = financialsOrZero(financials, String(row.id))
|
|
173
|
+
row.balance_due = values.balance_due
|
|
174
|
+
row.overdue_amount = values.overdue_amount
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
},
|
|
127
178
|
actions: {
|
|
128
179
|
create: {
|
|
129
180
|
commandId: 'billing.accounts.create',
|
|
@@ -190,12 +241,15 @@ export const openApi = createBillingCrudOpenApi({
|
|
|
190
241
|
create: {
|
|
191
242
|
schema: billingAccountCreateSchema,
|
|
192
243
|
responseSchema: defaultCreateResponseSchema,
|
|
193
|
-
description:
|
|
244
|
+
description:
|
|
245
|
+
'Creates a new billing account for a customer — a company or a person (`customerKind`, default `company`). ' +
|
|
246
|
+
'`invoiceLanguage` must be a language supported by the invoice template (`en`, `pl`; default `en`).',
|
|
194
247
|
},
|
|
195
248
|
update: {
|
|
196
249
|
schema: billingAccountUpdateSchema,
|
|
197
250
|
responseSchema: defaultOkResponseSchema,
|
|
198
|
-
description:
|
|
251
|
+
description:
|
|
252
|
+
'Updates a billing account. `currencyCode`, `customerId` and `customerKind` are immutable.',
|
|
199
253
|
},
|
|
200
254
|
del: {
|
|
201
255
|
schema: z.object({ id: z.string().uuid() }),
|
|
@@ -23,8 +23,7 @@ import {
|
|
|
23
23
|
* - Every active + soft-deleted `BillingItem` for the account.
|
|
24
24
|
* - Every `BillingAccountUsage` record.
|
|
25
25
|
* - Every `BillingRunOutcome` row referencing the account.
|
|
26
|
-
* - Every `
|
|
27
|
-
* (matched via `metadata->>'bill_account_id'`).
|
|
26
|
+
* - Every billing document (`billing_invoices`) of the account.
|
|
28
27
|
* - Every `BillingDraftInvoiceEdit` audit row for those invoices.
|
|
29
28
|
*
|
|
30
29
|
* Scope: the caller's auth tenant/organization is the authority — a
|
|
@@ -154,18 +153,18 @@ export async function GET(
|
|
|
154
153
|
),
|
|
155
154
|
])
|
|
156
155
|
|
|
157
|
-
//
|
|
158
|
-
// predicate keeps non-billing core/sales invoices out.
|
|
156
|
+
// Billing documents created in the window (Spec 0: billing_invoices).
|
|
159
157
|
const invoiceRows = (await em.execute(
|
|
160
|
-
`SELECT id,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
`SELECT id, number, document_type, series, status, test_run, bill_run_id, bill_date,
|
|
159
|
+
period_start, period_end, issue_date, due_date, posted_at, currency_code,
|
|
160
|
+
subtotal_net, tax_total, total_gross, paid_total, credited_total, outstanding,
|
|
161
|
+
corrects_invoice_id, correction_reason, topup_id, buyer_snapshot, seller_snapshot,
|
|
162
|
+
metadata, created_at, updated_at
|
|
163
|
+
FROM billing_invoices
|
|
165
164
|
WHERE tenant_id = ?
|
|
166
165
|
AND organization_id = ?
|
|
167
166
|
AND deleted_at IS NULL
|
|
168
|
-
AND
|
|
167
|
+
AND bill_account_id = ?
|
|
169
168
|
AND created_at >= ?
|
|
170
169
|
AND created_at <= ?
|
|
171
170
|
ORDER BY created_at DESC
|
|
@@ -190,7 +189,10 @@ export async function GET(
|
|
|
190
189
|
return NextResponse.json({
|
|
191
190
|
exportedAt: new Date().toISOString(),
|
|
192
191
|
// v2 adds prepaid balance / transactions / top-ups / statements (SPEC-002).
|
|
193
|
-
|
|
192
|
+
// v3 (Spec 0): invoices come from billing_invoices with billing field names.
|
|
193
|
+
// v4 (Spec B): items carry taxRate; the account carries paymentTermsDays /
|
|
194
|
+
// autoSendInvoice; invoice metadata may carry draft_number / deliveries.
|
|
195
|
+
schemaVersion: 4,
|
|
194
196
|
tenantId,
|
|
195
197
|
organizationId,
|
|
196
198
|
window: {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { readRequestBody, runCommandRoute } from '../../../../lib/commandRoute'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `POST /api/billing/invoices/[id]/memos` — create a DRAFT credit or debit
|
|
6
|
+
* memo correcting a posted billing invoice (Spec D). Post it with
|
|
7
|
+
* `POST /api/billing/invoices/post`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const metadata = {
|
|
11
|
+
POST: { requireAuth: true, requireFeatures: ['billing.invoice.correct'] },
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const lineSchema = z.object({
|
|
15
|
+
correctsLineId: z.string().uuid().optional(),
|
|
16
|
+
description: z.string().trim().min(1).max(500),
|
|
17
|
+
quantity: z.coerce.number().positive(),
|
|
18
|
+
unitPriceNet: z.coerce.number().nonnegative(),
|
|
19
|
+
taxRate: z.coerce.number().min(0).max(100).optional(),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const bodySchema = z.object({
|
|
23
|
+
invoiceId: z.string().uuid(),
|
|
24
|
+
type: z.enum(['credit_memo', 'debit_memo']),
|
|
25
|
+
reason: z.string().trim().min(1).max(1000),
|
|
26
|
+
lines: z.array(lineSchema).max(200).optional(),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
30
|
+
const { id } = await params
|
|
31
|
+
const raw = (await readRequestBody(req)) as Record<string, unknown>
|
|
32
|
+
return runCommandRoute(
|
|
33
|
+
req,
|
|
34
|
+
{
|
|
35
|
+
commandId: 'billing.invoices.create_memo',
|
|
36
|
+
bodySchema,
|
|
37
|
+
resourceKind: 'billing.invoice',
|
|
38
|
+
resourceId: (body) => body.invoiceId,
|
|
39
|
+
logLabel: 'billing/invoices/[id]/memos.POST',
|
|
40
|
+
},
|
|
41
|
+
{ ...(raw && typeof raw === 'object' ? raw : {}), invoiceId: id },
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const openApi = {
|
|
46
|
+
methods: {
|
|
47
|
+
POST: {
|
|
48
|
+
tags: ['Billing'],
|
|
49
|
+
summary: 'Create a credit or debit memo',
|
|
50
|
+
description:
|
|
51
|
+
'Creates a draft memo for a posted / partially paid / paid invoice (a fully credited invoice ' +
|
|
52
|
+
'accepts debit memos only). `reason` is required and printed. Credit memo without `lines` = full ' +
|
|
53
|
+
'credit of every original line; lines with `correctsLineId` keep the original VAT rate; new lines ' +
|
|
54
|
+
'default to the organization rate. Amounts are positive — the document type gives the sign.',
|
|
55
|
+
requestBody: { schema: bodySchema.omit({ invoiceId: true }) },
|
|
56
|
+
responses: [{ status: 200, description: '`{ memoId, number, documentType, totalGross }`' }],
|
|
57
|
+
errors: [
|
|
58
|
+
{ status: 409, description: '`billing.memo.invalid_original` / `billing.memo.exceeds_invoice`' },
|
|
59
|
+
{ status: 400, description: '`billing.memo.invalid_line`' },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
}
|