@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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
+
import { BillingSettings } from '../data/entities'
|
|
3
|
+
import { DEFAULT_NUMBER_MASKS, maskFor, type DocumentSeries } from './numbering'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Per-organization billing settings (Spec 0, E-D2 / E-D5).
|
|
7
|
+
*
|
|
8
|
+
* Replaces the tenant-wide core `configs` rows billing used to seed. One row
|
|
9
|
+
* per (tenant, organization), created lazily with today's defaults on first
|
|
10
|
+
* read so every caller can rely on it existing. Spec B adds the seller /
|
|
11
|
+
* payment / document fields (the INSERT below relies on their DB defaults:
|
|
12
|
+
* 14 days terms, 23 % VAT, seller fields empty).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export type SettingsScope = { tenantId: string; organizationId: string }
|
|
16
|
+
|
|
17
|
+
export const BILLING_SETTINGS_DEFAULTS = {
|
|
18
|
+
lowBalanceThresholdDefault: '0',
|
|
19
|
+
nearLimitBufferDefault: '0',
|
|
20
|
+
statementEnabled: true,
|
|
21
|
+
topupTaxRate: '0',
|
|
22
|
+
topupProvider: 'stripe',
|
|
23
|
+
numberMasks: { ...DEFAULT_NUMBER_MASKS } as Record<string, string>,
|
|
24
|
+
defaultPaymentTermsDays: 14,
|
|
25
|
+
defaultTaxRate: '23',
|
|
26
|
+
prepaidTaxPoint: 'topup',
|
|
27
|
+
} as const
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* VAT rate printed on top-up receipts (Spec F, P-D11): `topup_tax_rate` when
|
|
31
|
+
* the prepaid tax point is `topup`, 0 in `consumption` mode (VAT is charged
|
|
32
|
+
* on each debit instead — never twice).
|
|
33
|
+
*/
|
|
34
|
+
export function receiptTaxRate(settings: Pick<BillingSettings, 'topupTaxRate' | 'prepaidTaxPoint'>): number {
|
|
35
|
+
if (settings.prepaidTaxPoint === 'consumption') return 0
|
|
36
|
+
const n = Number.parseFloat(String(settings.topupTaxRate ?? '0'))
|
|
37
|
+
return Number.isFinite(n) && n > 0 ? n : 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** numeric(7,4) column value for a VAT percentage. */
|
|
41
|
+
export function taxRateColumn(ratePercent: number): string {
|
|
42
|
+
return (Math.round(ratePercent * 10000) / 10000).toString()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Seller fields a posted (printable) invoice requires — Spec B, P-D4. */
|
|
46
|
+
export const REQUIRED_SELLER_FIELDS = ['legalName', 'sellerAddress', 'taxId', 'iban'] as const
|
|
47
|
+
export type RequiredSellerField = (typeof REQUIRED_SELLER_FIELDS)[number]
|
|
48
|
+
|
|
49
|
+
export function missingSellerFields(
|
|
50
|
+
settings: Pick<BillingSettings, RequiredSellerField>,
|
|
51
|
+
): RequiredSellerField[] {
|
|
52
|
+
return REQUIRED_SELLER_FIELDS.filter((field) => {
|
|
53
|
+
const value = settings[field]
|
|
54
|
+
if (value === null || value === undefined) return true
|
|
55
|
+
if (typeof value === 'string') return value.trim() === ''
|
|
56
|
+
return Object.keys(value).length === 0
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Seller block frozen onto an invoice at post, so later settings edits never
|
|
62
|
+
* change an issued document.
|
|
63
|
+
*/
|
|
64
|
+
export function buildSellerSnapshot(settings: BillingSettings): Record<string, unknown> {
|
|
65
|
+
return {
|
|
66
|
+
organization_id: settings.organizationId,
|
|
67
|
+
legal_name: settings.legalName ?? null,
|
|
68
|
+
address: settings.sellerAddress ?? null,
|
|
69
|
+
tax_id: settings.taxId ?? null,
|
|
70
|
+
bank_name: settings.bankName ?? null,
|
|
71
|
+
iban: settings.iban ?? null,
|
|
72
|
+
bic: settings.bic ?? null,
|
|
73
|
+
invoice_footer: settings.invoiceFooter ?? null,
|
|
74
|
+
logo_attachment_id: settings.logoAttachmentId ?? null,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function getBillingSettings(em: EntityManager, scope: SettingsScope): Promise<BillingSettings> {
|
|
79
|
+
const where = { tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null }
|
|
80
|
+
const existing = await em.findOne(BillingSettings, where as never)
|
|
81
|
+
if (existing) return existing
|
|
82
|
+
// Race-safe create: a concurrent first read may insert the same row; the
|
|
83
|
+
// partial unique index makes ours a no-op and we re-read the winner.
|
|
84
|
+
const now = new Date()
|
|
85
|
+
await em.execute(
|
|
86
|
+
`INSERT INTO billing_settings
|
|
87
|
+
(tenant_id, organization_id, low_balance_threshold_default, near_limit_buffer_default,
|
|
88
|
+
statement_enabled, topup_tax_rate, topup_provider, number_masks, created_at, updated_at)
|
|
89
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?::jsonb, ?, ?)
|
|
90
|
+
ON CONFLICT (tenant_id, organization_id) WHERE deleted_at IS NULL DO NOTHING`,
|
|
91
|
+
[
|
|
92
|
+
scope.tenantId,
|
|
93
|
+
scope.organizationId,
|
|
94
|
+
BILLING_SETTINGS_DEFAULTS.lowBalanceThresholdDefault,
|
|
95
|
+
BILLING_SETTINGS_DEFAULTS.nearLimitBufferDefault,
|
|
96
|
+
BILLING_SETTINGS_DEFAULTS.statementEnabled,
|
|
97
|
+
BILLING_SETTINGS_DEFAULTS.topupTaxRate,
|
|
98
|
+
BILLING_SETTINGS_DEFAULTS.topupProvider,
|
|
99
|
+
JSON.stringify(BILLING_SETTINGS_DEFAULTS.numberMasks),
|
|
100
|
+
now,
|
|
101
|
+
now,
|
|
102
|
+
],
|
|
103
|
+
)
|
|
104
|
+
const created = await em.findOne(BillingSettings, where as never)
|
|
105
|
+
if (!created) throw new Error('Billing settings could not be initialised')
|
|
106
|
+
return created
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Mask for a series, falling back to the default when unset or invalid. */
|
|
110
|
+
export function numberMaskFor(settings: Pick<BillingSettings, 'numberMasks'>, series: DocumentSeries): string {
|
|
111
|
+
return maskFor(settings.numberMasks ?? null, series)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function numberOr(value: string | number | null | undefined, fallback: number): number {
|
|
115
|
+
const n = typeof value === 'number' ? value : Number.parseFloat(value ?? '')
|
|
116
|
+
return Number.isFinite(n) ? n : fallback
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Plain view of the settings — what the API returns and the UI edits. */
|
|
120
|
+
export function serializeBillingSettings(settings: BillingSettings) {
|
|
121
|
+
return {
|
|
122
|
+
id: settings.id,
|
|
123
|
+
lowBalanceThresholdDefault: numberOr(settings.lowBalanceThresholdDefault, 0),
|
|
124
|
+
nearLimitBufferDefault: numberOr(settings.nearLimitBufferDefault, 0),
|
|
125
|
+
statementEnabled: settings.statementEnabled,
|
|
126
|
+
topupTaxRate: numberOr(settings.topupTaxRate, 0),
|
|
127
|
+
prepaidTaxPoint: settings.prepaidTaxPoint === 'consumption' ? ('consumption' as const) : ('topup' as const),
|
|
128
|
+
topupProvider: settings.topupProvider,
|
|
129
|
+
numberMasks: {
|
|
130
|
+
invoice: numberMaskFor(settings, 'invoice'),
|
|
131
|
+
credit_memo: numberMaskFor(settings, 'credit_memo'),
|
|
132
|
+
receipt: numberMaskFor(settings, 'receipt'),
|
|
133
|
+
},
|
|
134
|
+
legalName: settings.legalName ?? '',
|
|
135
|
+
sellerAddress: settings.sellerAddress ?? null,
|
|
136
|
+
taxId: settings.taxId ?? '',
|
|
137
|
+
bankName: settings.bankName ?? '',
|
|
138
|
+
iban: settings.iban ?? '',
|
|
139
|
+
bic: settings.bic ?? '',
|
|
140
|
+
invoiceFooter: settings.invoiceFooter ?? '',
|
|
141
|
+
logoAttachmentId: settings.logoAttachmentId ?? null,
|
|
142
|
+
defaultPaymentTermsDays: settings.defaultPaymentTermsDays ?? BILLING_SETTINGS_DEFAULTS.defaultPaymentTermsDays,
|
|
143
|
+
defaultTaxRate: numberOr(settings.defaultTaxRate, 23),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import type { ZodType } from 'zod'
|
|
3
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
4
|
+
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
5
|
+
import {
|
|
6
|
+
organizationScopeRequiredResponse,
|
|
7
|
+
resolveActiveOrganizationId,
|
|
8
|
+
} from '@open-mercato/shared/lib/auth/organizationScope'
|
|
9
|
+
import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
|
|
10
|
+
import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
11
|
+
import { bridgeLegacyGuard, runMutationGuards } from '@open-mercato/shared/lib/crud/mutation-guard-registry'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Shared handler for billing's custom POST action routes (Spec C payments,
|
|
15
|
+
* Spec D memos): authenticates, resolves the organization, validates the
|
|
16
|
+
* body, runs the shared mutation guards (record locks etc.) as `update` on
|
|
17
|
+
* the target resource, executes the command and maps `CrudHttpError`s.
|
|
18
|
+
*
|
|
19
|
+
* `requireFeatures` stays on each route's exported `metadata` (enforced by
|
|
20
|
+
* the API dispatcher).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export type CommandRouteOptions<TBody> = {
|
|
24
|
+
commandId: string
|
|
25
|
+
/** Body schema WITHOUT tenant / organization (they come from auth). */
|
|
26
|
+
bodySchema: ZodType<TBody>
|
|
27
|
+
resourceKind: string
|
|
28
|
+
resourceId: (body: TBody) => string | null
|
|
29
|
+
/** Mutation-guard operation (default `update`, the usual one for action routes). */
|
|
30
|
+
operation?: 'create' | 'update' | 'delete'
|
|
31
|
+
/** Optional input mapping (default: body + scope). */
|
|
32
|
+
toInput?: (body: TBody, scope: { tenantId: string; organizationId: string }) => Record<string, unknown>
|
|
33
|
+
logLabel: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function userFeatures(auth: unknown): string[] {
|
|
37
|
+
const features = (auth as { features?: unknown })?.features
|
|
38
|
+
return Array.isArray(features) ? features.filter((value): value is string => typeof value === 'string') : []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function readRequestBody(req: Request): Promise<unknown> {
|
|
42
|
+
try {
|
|
43
|
+
return await req.json()
|
|
44
|
+
} catch {
|
|
45
|
+
return {}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function runCommandRoute<TBody>(
|
|
50
|
+
req: Request,
|
|
51
|
+
options: CommandRouteOptions<TBody>,
|
|
52
|
+
rawBody?: unknown,
|
|
53
|
+
): Promise<Response> {
|
|
54
|
+
try {
|
|
55
|
+
const container = await createRequestContainer()
|
|
56
|
+
const auth = await getAuthFromRequest(req)
|
|
57
|
+
if (!auth || !auth.tenantId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
58
|
+
const organizationId = resolveActiveOrganizationId(auth)
|
|
59
|
+
if (!organizationId) return organizationScopeRequiredResponse()
|
|
60
|
+
const parsed = options.bodySchema.safeParse(rawBody === undefined ? await readRequestBody(req) : rawBody)
|
|
61
|
+
if (!parsed.success) {
|
|
62
|
+
return NextResponse.json(
|
|
63
|
+
{ error: 'Invalid request', code: 'billing.request.invalid', issues: parsed.error.issues },
|
|
64
|
+
{ status: 400 },
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
const scope = { tenantId: auth.tenantId, organizationId }
|
|
68
|
+
const guardInput = {
|
|
69
|
+
tenantId: auth.tenantId,
|
|
70
|
+
organizationId,
|
|
71
|
+
userId: auth.sub ?? '',
|
|
72
|
+
resourceKind: options.resourceKind,
|
|
73
|
+
resourceId: options.resourceId(parsed.data),
|
|
74
|
+
operation: options.operation ?? ('update' as const),
|
|
75
|
+
requestMethod: req.method,
|
|
76
|
+
requestHeaders: req.headers,
|
|
77
|
+
}
|
|
78
|
+
const legacyGuard = bridgeLegacyGuard(container)
|
|
79
|
+
const guard = legacyGuard
|
|
80
|
+
? await runMutationGuards([legacyGuard], guardInput, { userFeatures: userFeatures(auth) })
|
|
81
|
+
: { ok: true as const, afterSuccessCallbacks: [] }
|
|
82
|
+
if (!guard.ok) {
|
|
83
|
+
return NextResponse.json(guard.errorBody ?? { error: 'Operation blocked by guard' }, { status: guard.errorStatus ?? 422 })
|
|
84
|
+
}
|
|
85
|
+
const ctx: CommandRuntimeContext = {
|
|
86
|
+
container,
|
|
87
|
+
auth,
|
|
88
|
+
organizationScope: null,
|
|
89
|
+
selectedOrganizationId: organizationId,
|
|
90
|
+
organizationIds: [organizationId],
|
|
91
|
+
request: req,
|
|
92
|
+
}
|
|
93
|
+
const commandBus = container.resolve('commandBus') as CommandBus
|
|
94
|
+
const input = options.toInput
|
|
95
|
+
? options.toInput(parsed.data, scope)
|
|
96
|
+
: { ...(parsed.data as Record<string, unknown>), ...scope }
|
|
97
|
+
const response = await commandBus.execute(options.commandId, { input, ctx })
|
|
98
|
+
for (const callback of guard.afterSuccessCallbacks) {
|
|
99
|
+
if (!callback.guard.afterSuccess) continue
|
|
100
|
+
try {
|
|
101
|
+
await callback.guard.afterSuccess({ ...guardInput, resourceId: guardInput.resourceId ?? '', metadata: callback.metadata ?? null })
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(`[${options.logLabel}] guard afterSuccess failed`, err)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return NextResponse.json((response as { result?: unknown }).result ?? { ok: true })
|
|
107
|
+
} catch (err) {
|
|
108
|
+
if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
|
|
109
|
+
console.error(`[${options.logLabel}]`, err)
|
|
110
|
+
return NextResponse.json({ error: 'Internal error' }, { status: 500 })
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { CustomerKind } from '../data/validators'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure helpers for linking a Billing Account to a customer record (Spec E).
|
|
5
|
+
*
|
|
6
|
+
* Billing never imports the customers module: `customer_id` is a string FK
|
|
7
|
+
* and every read goes through the customers REST API from the UI (a soft
|
|
8
|
+
* dependency — if it is unavailable the form degrades to a raw id and no
|
|
9
|
+
* prefill). These helpers keep the response-shape knowledge in one tested
|
|
10
|
+
* place so the React components stay thin.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type CustomerSummary = {
|
|
14
|
+
id: string
|
|
15
|
+
name: string
|
|
16
|
+
email: string | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type PrefillAddress = {
|
|
20
|
+
line1: string
|
|
21
|
+
line2: string
|
|
22
|
+
city: string
|
|
23
|
+
postal_code: string
|
|
24
|
+
country: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type PrefillTarget = {
|
|
28
|
+
name: string
|
|
29
|
+
invoiceEmail: string
|
|
30
|
+
invoiceAddress: PrefillAddress
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type RawRecord = Record<string, unknown>
|
|
34
|
+
|
|
35
|
+
const CUSTOMER_ENDPOINTS: Record<CustomerKind, string> = {
|
|
36
|
+
company: '/api/customers/companies',
|
|
37
|
+
person: '/api/customers/people',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const CUSTOMER_DETAIL_PATHS: Record<CustomerKind, string> = {
|
|
41
|
+
company: '/backend/customers/companies',
|
|
42
|
+
person: '/backend/customers/people',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Max ids per batched lookup — the CRUD factory caps `pageSize` at 100. */
|
|
46
|
+
export const CUSTOMER_LOOKUP_CHUNK = 100
|
|
47
|
+
|
|
48
|
+
export function customerEndpoint(kind: CustomerKind): string {
|
|
49
|
+
return CUSTOMER_ENDPOINTS[kind]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function customerDetailHref(kind: CustomerKind, customerId: string): string {
|
|
53
|
+
return `${CUSTOMER_DETAIL_PATHS[kind]}/${encodeURIComponent(customerId)}`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function str(value: unknown): string {
|
|
57
|
+
if (typeof value === 'string') return value.trim()
|
|
58
|
+
if (typeof value === 'number') return String(value)
|
|
59
|
+
return ''
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Extracts the row array from a list response (`{ items }` or a bare array). */
|
|
63
|
+
export function extractItems(result: unknown): RawRecord[] {
|
|
64
|
+
const raw = Array.isArray(result)
|
|
65
|
+
? result
|
|
66
|
+
: result && typeof result === 'object' && Array.isArray((result as { items?: unknown }).items)
|
|
67
|
+
? (result as { items: unknown[] }).items
|
|
68
|
+
: []
|
|
69
|
+
return raw.filter((row): row is RawRecord => !!row && typeof row === 'object')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Maps a customers list row (snake_case or camelCase) to a summary. */
|
|
73
|
+
export function toCustomerSummary(row: RawRecord): CustomerSummary | null {
|
|
74
|
+
const id = str(row.id)
|
|
75
|
+
if (!id) return null
|
|
76
|
+
const name = str(row.display_name) || str(row.displayName) || str(row.name) || id
|
|
77
|
+
const email = str(row.primary_email) || str(row.primaryEmail) || str(row.email)
|
|
78
|
+
return { id, name, email: email || null }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Picks the customer's primary address (else the first usable one) and maps
|
|
83
|
+
* it onto the billing `invoice_address` shape. Returns null when no row has
|
|
84
|
+
* the parts billing requires (line 1, city, postal code, country).
|
|
85
|
+
*/
|
|
86
|
+
export function mapPrimaryAddress(rows: RawRecord[]): PrefillAddress | null {
|
|
87
|
+
const ordered = [...rows].sort((a, b) => Number(b.is_primary === true) - Number(a.is_primary === true))
|
|
88
|
+
for (const row of ordered) {
|
|
89
|
+
const street = str(row.address_line1) || str(row.addressLine1)
|
|
90
|
+
const building = str(row.building_number) || str(row.buildingNumber)
|
|
91
|
+
const flat = str(row.flat_number) || str(row.flatNumber)
|
|
92
|
+
let line1 = street
|
|
93
|
+
if (line1 && building) line1 = `${line1} ${building}`
|
|
94
|
+
if (line1 && building && flat) line1 = `${line1}/${flat}`
|
|
95
|
+
const city = str(row.city)
|
|
96
|
+
const postalCode = str(row.postal_code) || str(row.postalCode)
|
|
97
|
+
const country = (str(row.country) || str(row.country_code) || str(row.countryCode)).toUpperCase()
|
|
98
|
+
if (!line1 || !city || !postalCode || country.length < 2 || country.length > 3) continue
|
|
99
|
+
return {
|
|
100
|
+
line1,
|
|
101
|
+
line2: str(row.address_line2) || str(row.addressLine2),
|
|
102
|
+
city,
|
|
103
|
+
postal_code: postalCode,
|
|
104
|
+
country,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isAddressEmpty(address: PrefillAddress): boolean {
|
|
111
|
+
return !address.line1.trim() && !address.line2.trim() && !address.city.trim() && !address.postal_code.trim()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Fills ONLY empty fields from the customer record (decision E2-D2) — never
|
|
116
|
+
* overwrites what the operator typed. The address is taken as a whole block
|
|
117
|
+
* (and only if the form's address is still blank) so we never mix a typed
|
|
118
|
+
* street with a prefilled city. Country alone does not count as "typed": the
|
|
119
|
+
* form ships a default country.
|
|
120
|
+
*/
|
|
121
|
+
export function applyCustomerPrefill<T extends PrefillTarget>(
|
|
122
|
+
current: T,
|
|
123
|
+
customer: CustomerSummary | null,
|
|
124
|
+
address: PrefillAddress | null,
|
|
125
|
+
): T {
|
|
126
|
+
let next = current
|
|
127
|
+
if (customer) {
|
|
128
|
+
if (!current.name.trim() && customer.name) next = { ...next, name: customer.name }
|
|
129
|
+
if (!current.invoiceEmail.trim() && customer.email) next = { ...next, invoiceEmail: customer.email }
|
|
130
|
+
}
|
|
131
|
+
if (address && isAddressEmpty(current.invoiceAddress)) {
|
|
132
|
+
next = { ...next, invoiceAddress: { ...address } }
|
|
133
|
+
}
|
|
134
|
+
return next
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Groups ids by kind and chunks them for batched `?ids=` lookups. */
|
|
138
|
+
export function planCustomerLookups(
|
|
139
|
+
rows: Array<{ customerId: string | null | undefined; customerKind: CustomerKind | null | undefined }>,
|
|
140
|
+
): Array<{ kind: CustomerKind; ids: string[] }> {
|
|
141
|
+
const byKind = new Map<CustomerKind, Set<string>>()
|
|
142
|
+
for (const row of rows) {
|
|
143
|
+
const id = (row.customerId ?? '').trim()
|
|
144
|
+
if (!id) continue
|
|
145
|
+
const kind: CustomerKind = row.customerKind === 'person' ? 'person' : 'company'
|
|
146
|
+
if (!byKind.has(kind)) byKind.set(kind, new Set())
|
|
147
|
+
byKind.get(kind)!.add(id)
|
|
148
|
+
}
|
|
149
|
+
const plan: Array<{ kind: CustomerKind; ids: string[] }> = []
|
|
150
|
+
for (const [kind, ids] of byKind) {
|
|
151
|
+
const all = Array.from(ids)
|
|
152
|
+
for (let i = 0; i < all.length; i += CUSTOMER_LOOKUP_CHUNK) {
|
|
153
|
+
plan.push({ kind, ids: all.slice(i, i + CUSTOMER_LOOKUP_CHUNK) })
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return plan
|
|
157
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date-only helpers (Spec F, P-D5).
|
|
3
|
+
*
|
|
4
|
+
* MikroORM v7 hydrates `date` columns as `YYYY-MM-DD` strings while the
|
|
5
|
+
* engine computes with `Date`s; and `pg` serialises a `Date` query
|
|
6
|
+
* parameter in the server's time zone, which can shift a `date` comparison
|
|
7
|
+
* by a day. Every date-only value the billing engine compares or sends to a
|
|
8
|
+
* `date` column goes through these helpers: compare as UTC midnight, query
|
|
9
|
+
* with the ISO day string.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** UTC-midnight `Date` for a `Date`, ISO string or timestamp. */
|
|
13
|
+
export function toUtcMidnight(value: Date | string | number): Date {
|
|
14
|
+
const date = typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value) ? new Date(`${value}T00:00:00Z`) : new Date(value)
|
|
15
|
+
date.setUTCHours(0, 0, 0, 0)
|
|
16
|
+
return date
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** `YYYY-MM-DD` (UTC) for a `Date`, ISO string or timestamp. */
|
|
20
|
+
export function toIsoDate(value: Date | string | number): string {
|
|
21
|
+
return toUtcMidnight(value).toISOString().slice(0, 10)
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BillingAccount } from '../data/entities'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parties printed on a billing document. Posting freezes them as snapshots
|
|
5
|
+
* on the invoice (buyer from the account, seller from `billingSettings`), so
|
|
6
|
+
* later account / settings edits never change an issued document; drafts
|
|
7
|
+
* render from the live rows.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Frozen "issued to" block — later account edits never change a posted document. */
|
|
11
|
+
export function buildBuyerSnapshot(account: BillingAccount): Record<string, unknown> {
|
|
12
|
+
return {
|
|
13
|
+
bill_account_id: account.id,
|
|
14
|
+
name: account.name,
|
|
15
|
+
customer_id: account.customerId,
|
|
16
|
+
customer_kind: account.customerKind ?? 'company',
|
|
17
|
+
tax_id: account.taxId ?? null,
|
|
18
|
+
invoice_email: account.invoiceEmail,
|
|
19
|
+
invoice_language: account.invoiceLanguage,
|
|
20
|
+
invoice_address: account.invoiceAddress ?? null,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billing document type helpers (Spec 0 / Spec D). Pure.
|
|
3
|
+
*
|
|
4
|
+
* invoice — receivable, `invoice` series
|
|
5
|
+
* debit_memo — receivable (supplementary invoice), `invoice` series (D-D8)
|
|
6
|
+
* credit_memo — credit source, `credit_memo` series; amounts stored positive (D-D5)
|
|
7
|
+
* receipt — prepaid top-up receipt, `receipt` series, never corrected (D-D6)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type DocumentType = 'invoice' | 'credit_memo' | 'debit_memo' | 'receipt'
|
|
11
|
+
|
|
12
|
+
export function isReceivable(type: string): boolean {
|
|
13
|
+
return type === 'invoice' || type === 'debit_memo'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isMemo(type: string): boolean {
|
|
17
|
+
return type === 'credit_memo' || type === 'debit_memo'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Numbering series of a document type. */
|
|
21
|
+
export function seriesFor(type: string): 'invoice' | 'credit_memo' | 'receipt' {
|
|
22
|
+
if (type === 'credit_memo') return 'credit_memo'
|
|
23
|
+
if (type === 'receipt') return 'receipt'
|
|
24
|
+
return 'invoice'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Sign used for DISPLAY only — credit memos read as negative amounts. */
|
|
28
|
+
export function displaySign(type: string): 1 | -1 {
|
|
29
|
+
return type === 'credit_memo' ? -1 : 1
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Originals a memo may correct (D-D3): posted, partially paid, paid (credited → debit only). */
|
|
33
|
+
export function canCorrect(original: { documentType: string; status: string; testRun: boolean }, memoType: string): boolean {
|
|
34
|
+
if (original.testRun || original.documentType !== 'invoice') return false
|
|
35
|
+
if (['posted', 'partially_paid', 'paid'].includes(original.status)) return true
|
|
36
|
+
return original.status === 'credited' && memoType === 'debit_memo'
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Due-date math for posted invoices (Spec B). Pure, date-only, UTC.
|
|
3
|
+
*
|
|
4
|
+
* `due_date = issue_date + payment terms (days)`, where the terms come from
|
|
5
|
+
* the account (`payment_terms_days`) and fall back to the organization
|
|
6
|
+
* default (`billing_settings.default_payment_terms_days`).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export function resolvePaymentTermsDays(
|
|
10
|
+
accountTermsDays: number | null | undefined,
|
|
11
|
+
defaultTermsDays: number | null | undefined,
|
|
12
|
+
): number {
|
|
13
|
+
if (typeof accountTermsDays === 'number' && Number.isInteger(accountTermsDays) && accountTermsDays >= 0) {
|
|
14
|
+
return accountTermsDays
|
|
15
|
+
}
|
|
16
|
+
if (typeof defaultTermsDays === 'number' && Number.isInteger(defaultTermsDays) && defaultTermsDays >= 0) {
|
|
17
|
+
return defaultTermsDays
|
|
18
|
+
}
|
|
19
|
+
return 14
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Issue date (any time of day) + N calendar days, at UTC midnight. */
|
|
23
|
+
export function computeDueDate(issueDate: Date, termsDays: number): Date {
|
|
24
|
+
const base = new Date(issueDate)
|
|
25
|
+
return new Date(Date.UTC(base.getUTCFullYear(), base.getUTCMonth(), base.getUTCDate() + termsDays))
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IBAN / BIC helpers for the seller's payment instructions (Spec B). Pure.
|
|
3
|
+
*
|
|
4
|
+
* `normalizeIban` strips spaces and upper-cases; `isValidIban` checks the
|
|
5
|
+
* structure (country code, check digits, 11–30 alphanumerics after) and the
|
|
6
|
+
* ISO 13616 mod-97 checksum. Country-specific lengths are not enforced — the
|
|
7
|
+
* checksum already rejects typos, and a missing country table never blocks a
|
|
8
|
+
* legitimate account.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export function normalizeIban(value: string): string {
|
|
12
|
+
return value.replace(/\s+/g, '').toUpperCase()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isValidIban(value: string): boolean {
|
|
16
|
+
const iban = normalizeIban(value)
|
|
17
|
+
if (!/^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/.test(iban)) return false
|
|
18
|
+
const rearranged = iban.slice(4) + iban.slice(0, 4)
|
|
19
|
+
let remainder = 0
|
|
20
|
+
for (const char of rearranged) {
|
|
21
|
+
const digits = /[A-Z]/.test(char) ? String(char.charCodeAt(0) - 55) : char
|
|
22
|
+
for (const digit of digits) {
|
|
23
|
+
remainder = (remainder * 10 + Number(digit)) % 97
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return remainder === 1
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Groups of 4 for display ("PL61 1090 1014 …"). */
|
|
30
|
+
export function formatIban(value: string): string {
|
|
31
|
+
return normalizeIban(value).replace(/(.{4})/g, '$1 ').trim()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isValidBic(value: string): boolean {
|
|
35
|
+
return /^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$/.test(value.trim().toUpperCase())
|
|
36
|
+
}
|