@dainamite/billing 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +172 -0
- package/README.md +14 -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 +16 -12
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +44 -7
- 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 +3 -3
- package/dist/modules/billing/api/runs/retry-failed/route.js.map +2 -2
- package/dist/modules/billing/api/runs/route.js +11 -5
- 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/ce.js.map +2 -2
- 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 +272 -170
- 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 +154 -7
- 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 +615 -59
- 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 +70 -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 +241 -106
- package/dist/modules/billing/lib/billRunEngine.js.map +2 -2
- package/dist/modules/billing/lib/billRunLabel.js.map +1 -1
- 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/tenantLock.js.map +1 -1
- 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/dist/modules/billing/workers/reap-stale-bill-runs.js +2 -2
- package/dist/modules/billing/workers/reap-stale-bill-runs.js.map +2 -2
- package/generated/entities/billing_account/index.ts +26 -0
- package/generated/entities/billing_account_balance/index.ts +13 -0
- package/generated/entities/billing_account_transaction/index.ts +21 -0
- package/generated/entities/billing_account_usage/index.ts +18 -0
- package/generated/entities/billing_allocation/index.ts +18 -0
- package/generated/entities/billing_document_sequence/index.ts +11 -0
- package/generated/entities/billing_draft_invoice_edit/index.ts +14 -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 +26 -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 +20 -0
- package/generated/entities/billing_run_outcome/index.ts +18 -0
- package/generated/entities/billing_settings/index.ts +26 -0
- package/generated/entities/billing_statement/index.ts +22 -0
- package/generated/entities/billing_topup/index.ts +20 -0
- package/generated/entities.ids.generated.ts +35 -0
- package/package.json +15 -10
- 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 +437 -355
- 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 +326 -0
- package/src/modules/billing/__tests__/events.test.ts +142 -0
- 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 +231 -20
- 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 +20 -18
- 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 +3 -3
- package/src/modules/billing/api/invoices/delete-draft/route.ts +33 -0
- package/src/modules/billing/api/invoices/edit-line/route.ts +4 -4
- package/src/modules/billing/api/invoices/post/route.ts +7 -6
- package/src/modules/billing/api/invoices/remove-line/route.ts +3 -3
- 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 +50 -7
- 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 +4 -3
- package/src/modules/billing/api/runs/route.ts +17 -9
- 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 +71 -5
- package/src/modules/billing/backend/billing/invoices/[id]/page.tsx +445 -96
- 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 +19 -3
- 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 +50 -19
- package/src/modules/billing/backend/billing/runs/page.tsx +60 -9
- package/src/modules/billing/backend/billing/statements/[id]/page.tsx +87 -6
- package/src/modules/billing/backend/billing/statements/page.tsx +2 -2
- package/src/modules/billing/backend/billing/topups/page.tsx +2 -2
- package/src/modules/billing/backend/billing/transactions/page.tsx +2 -2
- 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/ce.ts +8 -7
- 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 +371 -238
- 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 +208 -6
- 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 +20 -7
- 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 +685 -15
- package/src/modules/billing/data/entityIds.ts +25 -11
- package/src/modules/billing/data/validators.ts +388 -8
- package/src/modules/billing/events.ts +82 -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 +362 -162
- package/src/modules/billing/lib/billRunLabel.ts +1 -1
- 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 +410 -242
- 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/tenantLock.ts +1 -1
- 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/src/modules/billing/workers/reap-stale-bill-runs.ts +4 -4
- 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,118 @@
|
|
|
1
|
+
import { buildVatSummary, resolvePrepaidDebit } from '../../lib/prepaidTaxPoint'
|
|
2
|
+
import { receiptTaxRate } from '../../lib/billingSettings'
|
|
3
|
+
import { consumePrepaidUsage } from '../../lib/prepaidConsume'
|
|
4
|
+
import { createPrepaidMockEm } from '../helpers/prepaidMockEm'
|
|
5
|
+
|
|
6
|
+
describe('resolvePrepaidDebit (Spec F, F-D3)', () => {
|
|
7
|
+
it('topup: debits the amount as entered — rate and basis ignored', () => {
|
|
8
|
+
expect(resolvePrepaidDebit({ lineAmount: 49, priceBasis: 'net', taxRate: 23, taxPoint: 'topup' })).toEqual({
|
|
9
|
+
debit: 49,
|
|
10
|
+
net: 49,
|
|
11
|
+
tax: 0,
|
|
12
|
+
taxRate: 0,
|
|
13
|
+
})
|
|
14
|
+
expect(resolvePrepaidDebit({ lineAmount: '12.30', priceBasis: 'gross', taxRate: 23, taxPoint: 'topup' }).debit).toBe(12.3)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('consumption: debits the gross — 20 @ 0 % → 20, 10 net @ 23 % → 12.30', () => {
|
|
18
|
+
expect(resolvePrepaidDebit({ lineAmount: 20, priceBasis: 'net', taxRate: 0, taxPoint: 'consumption' }).debit).toBe(20)
|
|
19
|
+
expect(resolvePrepaidDebit({ lineAmount: 10, priceBasis: 'net', taxRate: 23, taxPoint: 'consumption' })).toEqual({
|
|
20
|
+
debit: 12.3,
|
|
21
|
+
net: 10,
|
|
22
|
+
tax: 2.3,
|
|
23
|
+
taxRate: 23,
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('consumption: a gross price is debited as typed; credits keep their sign', () => {
|
|
28
|
+
expect(resolvePrepaidDebit({ lineAmount: 12.3, priceBasis: 'gross', taxRate: 23, taxPoint: 'consumption' }).debit).toBe(12.3)
|
|
29
|
+
expect(resolvePrepaidDebit({ lineAmount: -10, priceBasis: 'net', taxRate: 23, taxPoint: 'consumption' }).debit).toBe(-12.3)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('buildVatSummary', () => {
|
|
34
|
+
it('groups consumption debits by rate (highest first) and is empty in topup mode', () => {
|
|
35
|
+
const debits = [
|
|
36
|
+
resolvePrepaidDebit({ lineAmount: 10, priceBasis: 'net', taxRate: 23, taxPoint: 'consumption' }),
|
|
37
|
+
resolvePrepaidDebit({ lineAmount: 5, priceBasis: 'net', taxRate: 23, taxPoint: 'consumption' }),
|
|
38
|
+
resolvePrepaidDebit({ lineAmount: 20, priceBasis: 'net', taxRate: 0, taxPoint: 'consumption' }),
|
|
39
|
+
]
|
|
40
|
+
expect(buildVatSummary(debits, 'consumption')).toEqual([
|
|
41
|
+
{ rate: '23', net: '15.0000', tax: '3.4500', gross: '18.4500' },
|
|
42
|
+
{ rate: '0', net: '20.0000', tax: '0.0000', gross: '20.0000' },
|
|
43
|
+
])
|
|
44
|
+
expect(buildVatSummary(debits, 'topup')).toEqual([])
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('receiptTaxRate (Spec F, P-D11)', () => {
|
|
49
|
+
it('uses the top-up rate only when tax is charged at top-up — never twice', () => {
|
|
50
|
+
expect(receiptTaxRate({ topupTaxRate: '23', prepaidTaxPoint: 'topup' })).toBe(23)
|
|
51
|
+
expect(receiptTaxRate({ topupTaxRate: '23', prepaidTaxPoint: 'consumption' })).toBe(0)
|
|
52
|
+
expect(receiptTaxRate({ topupTaxRate: 'x', prepaidTaxPoint: 'topup' })).toBe(0)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('consumePrepaidUsage — tax point', () => {
|
|
57
|
+
const TENANT = 'ten-1'
|
|
58
|
+
const ORG = 'org-1'
|
|
59
|
+
const ACCT = 'acct-1'
|
|
60
|
+
const account = { id: ACCT, tenantId: TENANT, organizationId: ORG, currencyCode: 'EUR', billingMode: 'prepaid', creditLimit: '0' }
|
|
61
|
+
const usageItem = (overrides: Record<string, unknown> = {}) => ({
|
|
62
|
+
id: 'item-usage',
|
|
63
|
+
type: 'usage',
|
|
64
|
+
uomCode: 'gb',
|
|
65
|
+
isActive: true,
|
|
66
|
+
deletedAt: null,
|
|
67
|
+
taxRate: '23',
|
|
68
|
+
priceBasis: 'net',
|
|
69
|
+
rateJson: { unit_price: 1 },
|
|
70
|
+
description: 'Data',
|
|
71
|
+
...overrides,
|
|
72
|
+
})
|
|
73
|
+
const input = {
|
|
74
|
+
organizationId: ORG,
|
|
75
|
+
tenantId: TENANT,
|
|
76
|
+
billAccountId: ACCT,
|
|
77
|
+
uomCode: 'gb',
|
|
78
|
+
quantity: 10,
|
|
79
|
+
periodStart: new Date('2026-11-01'),
|
|
80
|
+
periodEnd: new Date('2026-11-30'),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
it('topup mode: debits the rated amount', async () => {
|
|
84
|
+
const em = createPrepaidMockEm({
|
|
85
|
+
account,
|
|
86
|
+
balance: { tenantId: TENANT, billAccountId: ACCT, balance: '100.0000' },
|
|
87
|
+
usageItem: usageItem(),
|
|
88
|
+
})
|
|
89
|
+
const { result } = await consumePrepaidUsage(em as never, account as never, input)
|
|
90
|
+
expect(result.ratedAmount).toBe('10.0000')
|
|
91
|
+
expect(result.balance).toBe('90.0000')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('consumption mode: debits the gross (10 × 1.00 net @ 23 % → 12.30) and records the split', async () => {
|
|
95
|
+
const em = createPrepaidMockEm({
|
|
96
|
+
account,
|
|
97
|
+
balance: { tenantId: TENANT, billAccountId: ACCT, balance: '100.0000' },
|
|
98
|
+
usageItem: usageItem(),
|
|
99
|
+
configs: { 'prepaid.tax_point': 'consumption' },
|
|
100
|
+
})
|
|
101
|
+
const { result } = await consumePrepaidUsage(em as never, account as never, input)
|
|
102
|
+
expect(result.ratedAmount).toBe('12.3000')
|
|
103
|
+
expect(result.balance).toBe('87.7000')
|
|
104
|
+
const txn = em.transactions.find((t) => t.type === 'usage')!
|
|
105
|
+
expect(txn.metadata).toMatchObject({ rated_amount: '10.0000', net: '10.0000', tax: '2.3000', tax_rate: '23', tax_point: 'consumption' })
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('consumption mode at 0 %: net = gross', async () => {
|
|
109
|
+
const em = createPrepaidMockEm({
|
|
110
|
+
account,
|
|
111
|
+
balance: { tenantId: TENANT, billAccountId: ACCT, balance: '100.0000' },
|
|
112
|
+
usageItem: usageItem({ taxRate: '0' }),
|
|
113
|
+
configs: { 'prepaid.tax_point': 'consumption' },
|
|
114
|
+
})
|
|
115
|
+
const { result } = await consumePrepaidUsage(em as never, account as never, input)
|
|
116
|
+
expect(result.ratedAmount).toBe('10.0000')
|
|
117
|
+
})
|
|
118
|
+
})
|
|
@@ -9,11 +9,15 @@ function makeMockEm() {
|
|
|
9
9
|
const store: Array<Record<string, unknown>> = []
|
|
10
10
|
let idSeq = 0
|
|
11
11
|
const nativeDeleteCalls: unknown[] = []
|
|
12
|
+
// Spec B: the seed fills empty seller fields on the org's BillingSettings.
|
|
13
|
+
const settings: Record<string, unknown> = { defaultTaxRate: '23', legalName: null }
|
|
12
14
|
|
|
13
15
|
const em = {
|
|
14
16
|
store,
|
|
15
17
|
nativeDeleteCalls,
|
|
16
|
-
|
|
18
|
+
settings,
|
|
19
|
+
findOne: jest.fn(async (entity: unknown, where: Record<string, unknown>) => {
|
|
20
|
+
if ((entity as { name?: string }).name === 'BillingSettings') return settings
|
|
17
21
|
return (
|
|
18
22
|
store.find((row) => {
|
|
19
23
|
if (typeof where.name === 'string') return row.name === where.name
|
|
@@ -38,6 +42,7 @@ function makeMockEm() {
|
|
|
38
42
|
return em as never as Parameters<typeof seedBillingDemo>[0] & {
|
|
39
43
|
store: Array<Record<string, unknown>>
|
|
40
44
|
nativeDeleteCalls: unknown[]
|
|
45
|
+
settings: Record<string, unknown>
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -49,7 +54,34 @@ describe('seedBillingDemo', () => {
|
|
|
49
54
|
const em = makeMockEm()
|
|
50
55
|
const res = await seedBillingDemo(em, SCOPE, { now: NOW })
|
|
51
56
|
|
|
52
|
-
expect(res).toEqual({ accountsCreated:
|
|
57
|
+
expect(res).toEqual({ accountsCreated: 3, itemsCreated: 6, usageCreated: 1 })
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('dates items to demo in-advance, in-arrears and mid-cycle proration (Spec A)', async () => {
|
|
61
|
+
const em = makeMockEm()
|
|
62
|
+
await seedBillingDemo(em, SCOPE, { now: NOW })
|
|
63
|
+
const byRef = (ref: string) =>
|
|
64
|
+
em.store.find((r) => r.sourceRef === ref) as { billStartDate: Date; chargeTiming: string } | undefined
|
|
65
|
+
const day = (d?: Date) => d?.toISOString().slice(0, 10)
|
|
66
|
+
expect(day(byRef('demo-billing-nw-rec')?.billStartDate)).toBe('2026-06-01')
|
|
67
|
+
expect(byRef('demo-billing-nw-rec')?.chargeTiming).toBe('in_advance')
|
|
68
|
+
expect(day(byRef('demo-billing-nw-addon')?.billStartDate)).toBe('2026-05-15')
|
|
69
|
+
expect(byRef('demo-billing-gx-arr')).toMatchObject({ chargeTiming: 'in_arrears' })
|
|
70
|
+
expect(day(byRef('demo-billing-gx-arr')?.billStartDate)).toBe('2026-05-01')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('seeds a B2C person account invoiced in Polish next to the company accounts', async () => {
|
|
74
|
+
const em = makeMockEm()
|
|
75
|
+
await seedBillingDemo(em, SCOPE, { now: NOW })
|
|
76
|
+
|
|
77
|
+
const person = em.store.find((r) => r.name === 'Demo — Jan Kowalski') as
|
|
78
|
+
| { customerKind: string; invoiceLanguage: string }
|
|
79
|
+
| undefined
|
|
80
|
+
const company = em.store.find((r) => r.name === 'Demo — Northwind Telecom') as
|
|
81
|
+
| { customerKind: string }
|
|
82
|
+
| undefined
|
|
83
|
+
expect(person).toMatchObject({ customerKind: 'person', invoiceLanguage: 'pl' })
|
|
84
|
+
expect(company?.customerKind).toBe('company')
|
|
53
85
|
})
|
|
54
86
|
|
|
55
87
|
it('is idempotent — a second run creates nothing', async () => {
|
|
@@ -83,4 +115,23 @@ describe('seedBillingDemo', () => {
|
|
|
83
115
|
await resetBillingDemo(em, SCOPE)
|
|
84
116
|
expect(em.nativeDelete).toHaveBeenCalledTimes(3)
|
|
85
117
|
})
|
|
118
|
+
|
|
119
|
+
it('seeds VAT rates on items, including a 0 % item (Spec B)', async () => {
|
|
120
|
+
const em = makeMockEm()
|
|
121
|
+
await seedBillingDemo(em, SCOPE, { now: NOW })
|
|
122
|
+
const rates = em.store.filter((r) => typeof r.sourceRef === 'string' && 'taxRate' in r).map((r) => r.taxRate)
|
|
123
|
+
expect(rates).toContain('23')
|
|
124
|
+
expect(rates).toContain('8')
|
|
125
|
+
expect(rates).toContain('0')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('fills empty seller settings so demo invoices can be posted, never overwriting real values', async () => {
|
|
129
|
+
const em = makeMockEm()
|
|
130
|
+
em.settings.taxId = 'PL-REAL'
|
|
131
|
+
await seedBillingDemo(em, SCOPE, { now: NOW })
|
|
132
|
+
expect(em.settings.legalName).toBe('Dainamite Demo sp. z o.o.')
|
|
133
|
+
expect(em.settings.iban).toBe('PL61109010140000071219812874')
|
|
134
|
+
expect(em.settings.sellerAddress).toMatchObject({ city: 'Kraków' })
|
|
135
|
+
expect(em.settings.taxId).toBe('PL-REAL')
|
|
136
|
+
})
|
|
86
137
|
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DEFAULT_DEMO_SELLER, applySellerDefaults, sellerFieldsToFill } from '../../lib/seeds/sellerSettings'
|
|
2
|
+
import { billingSettingsUpdateSchema } from '../../data/validators'
|
|
3
|
+
import { missingSellerFields } from '../../lib/billingSettings'
|
|
4
|
+
|
|
5
|
+
const scope = { tenantId: '11111111-1111-4111-8111-111111111111', organizationId: '22222222-2222-4222-8222-222222222222' }
|
|
6
|
+
|
|
7
|
+
describe('seller defaults for seed tenants', () => {
|
|
8
|
+
it('fills only empty fields — real details are never overwritten', () => {
|
|
9
|
+
const fill = sellerFieldsToFill(
|
|
10
|
+
{ legalName: 'Real Co', sellerAddress: null, taxId: '', bankName: null, iban: 'PL61109010140000071219812874', bic: null, invoiceFooter: null } as never,
|
|
11
|
+
DEFAULT_DEMO_SELLER,
|
|
12
|
+
)
|
|
13
|
+
expect(Object.keys(fill).sort()).toEqual(['bankName', 'bic', 'invoiceFooter', 'sellerAddress', 'taxId'])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('applies and flushes once; a re-run is a no-op', async () => {
|
|
17
|
+
const settings: Record<string, unknown> = { legalName: null, sellerAddress: null, taxId: null, bankName: null, iban: null, bic: null, invoiceFooter: null }
|
|
18
|
+
const em = { findOne: jest.fn(async () => settings), flush: jest.fn(async () => undefined), execute: jest.fn() }
|
|
19
|
+
expect(await applySellerDefaults(em as never, scope, DEFAULT_DEMO_SELLER)).toHaveLength(7)
|
|
20
|
+
expect(missingSellerFields(settings as never)).toEqual([])
|
|
21
|
+
expect(await applySellerDefaults(em as never, scope, DEFAULT_DEMO_SELLER)).toEqual([])
|
|
22
|
+
expect(em.flush).toHaveBeenCalledTimes(1)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('the default profile passes the settings validators (the page can save it unchanged)', () => {
|
|
26
|
+
expect(billingSettingsUpdateSchema.safeParse({ ...scope, ...DEFAULT_DEMO_SELLER }).success).toBe(true)
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_PERIODS_PER_ITEM,
|
|
3
|
+
computeRecurringLines,
|
|
4
|
+
gridPeriodContaining,
|
|
5
|
+
inclusiveDays,
|
|
6
|
+
roundHalfUp2dp,
|
|
7
|
+
type RecurringItemState,
|
|
8
|
+
} from '../../lib/servicePeriods'
|
|
9
|
+
import type { BillCycle } from '../../data/validators'
|
|
10
|
+
|
|
11
|
+
const d = (iso: string) => new Date(`${iso}T00:00:00.000Z`)
|
|
12
|
+
const iso = (date: Date | null) => (date ? date.toISOString().slice(0, 10) : null)
|
|
13
|
+
|
|
14
|
+
function item(overrides: Partial<RecurringItemState> = {}): RecurringItemState {
|
|
15
|
+
return {
|
|
16
|
+
id: 'item-1',
|
|
17
|
+
chargeTiming: 'in_advance',
|
|
18
|
+
prorate: true,
|
|
19
|
+
billStartDate: d('2026-11-14'),
|
|
20
|
+
billEndDate: null,
|
|
21
|
+
billedToDate: null,
|
|
22
|
+
unitPrice: 100,
|
|
23
|
+
isActive: true,
|
|
24
|
+
deletedAt: null,
|
|
25
|
+
...overrides,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function run(state: RecurringItemState, billDate: string, cycle: BillCycle = 'monthly') {
|
|
30
|
+
const res = computeRecurringLines(state, { billDate: d(billDate), cycle })
|
|
31
|
+
return {
|
|
32
|
+
...res,
|
|
33
|
+
periods: res.lines.map((l) => [iso(l.servicePeriodStart), iso(l.servicePeriodEnd), l.amount]),
|
|
34
|
+
watermark: iso(res.newBilledToDate),
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('helpers', () => {
|
|
39
|
+
it('counts inclusive days and rounds half-up symmetrically', () => {
|
|
40
|
+
expect(inclusiveDays(d('2026-11-14'), d('2026-11-30'))).toBe(17)
|
|
41
|
+
expect(roundHalfUp2dp(1.005)).toBe(1.01)
|
|
42
|
+
expect(roundHalfUp2dp(-1.005)).toBe(-1.01)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('finds the grid period containing a date, without month-end drift', () => {
|
|
46
|
+
const p = gridPeriodContaining(d('2026-01-31'), 'monthly', d('2026-03-31'))
|
|
47
|
+
expect([iso(p.start), iso(p.end)]).toEqual(['2026-03-31', '2026-04-29'])
|
|
48
|
+
const back = gridPeriodContaining(d('2026-12-01'), 'monthly', d('2026-11-14'))
|
|
49
|
+
expect([iso(back.start), iso(back.end)]).toEqual(['2026-11-01', '2026-11-30'])
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('computeRecurringLines — spec worked examples', () => {
|
|
54
|
+
it("operator's test: in advance from Nov 14 bills Nov 14–Dec 13 on Nov 14", () => {
|
|
55
|
+
const r = run(item(), '2026-11-14')
|
|
56
|
+
expect(r.periods).toEqual([['2026-11-14', '2026-12-13', 100]])
|
|
57
|
+
expect(r.watermark).toBe('2026-12-13')
|
|
58
|
+
expect(r.lines[0].proration).toBeNull()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('catch-up chain: Dec 14 and Jan 14 continue from the watermark', () => {
|
|
62
|
+
const dec = run(item({ billedToDate: d('2026-12-13') }), '2026-12-14')
|
|
63
|
+
expect(dec.periods).toEqual([['2026-12-14', '2027-01-13', 100]])
|
|
64
|
+
const jan = run(item({ billedToDate: d('2027-01-13') }), '2027-01-14')
|
|
65
|
+
expect(jan.periods).toEqual([['2027-01-14', '2027-02-13', 100]])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('mid-cycle add: prorated stub + full next period', () => {
|
|
69
|
+
const r = run(item(), '2026-12-01')
|
|
70
|
+
expect(r.periods).toEqual([
|
|
71
|
+
['2026-11-14', '2026-11-30', 56.67],
|
|
72
|
+
['2026-12-01', '2026-12-31', 100],
|
|
73
|
+
])
|
|
74
|
+
expect(r.lines[0].proration).toEqual({ activeDays: 17, periodDays: 30 })
|
|
75
|
+
expect(r.watermark).toBe('2026-12-31')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('in arrears bills the period just ended', () => {
|
|
79
|
+
const r = run(item({ chargeTiming: 'in_arrears', billStartDate: d('2026-11-01') }), '2026-12-01')
|
|
80
|
+
expect(r.periods).toEqual([['2026-11-01', '2026-11-30', 100]])
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('cancel after prepay: credit for unused days, watermark pulled back', () => {
|
|
84
|
+
const r = run(item({ billStartDate: d('2026-01-01'), billedToDate: d('2026-12-31'), billEndDate: d('2026-12-15') }), '2027-01-01')
|
|
85
|
+
expect(r.periods).toEqual([['2026-12-16', '2026-12-31', -51.61]])
|
|
86
|
+
expect(r.lines[0].kind).toBe('credit')
|
|
87
|
+
expect(r.watermark).toBe('2026-12-15')
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe('computeRecurringLines — edge cases', () => {
|
|
92
|
+
it('nothing to bill before the item starts (in arrears, start in the future)', () => {
|
|
93
|
+
const r = run(item({ chargeTiming: 'in_arrears', billStartDate: d('2026-12-05') }), '2026-12-01')
|
|
94
|
+
expect(r.lines).toHaveLength(0)
|
|
95
|
+
expect(r.watermark).toBeNull()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('backdated item spanning 3 grid periods lands on one bill date', () => {
|
|
99
|
+
const r = run(item({ chargeTiming: 'in_arrears', billStartDate: d('2026-09-01') }), '2026-12-01')
|
|
100
|
+
expect(r.periods).toEqual([
|
|
101
|
+
['2026-09-01', '2026-09-30', 100],
|
|
102
|
+
['2026-10-01', '2026-10-31', 100],
|
|
103
|
+
['2026-11-01', '2026-11-30', 100],
|
|
104
|
+
])
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('prorates a mid-period end (in arrears)', () => {
|
|
108
|
+
const r = run(
|
|
109
|
+
item({ chargeTiming: 'in_arrears', billStartDate: d('2026-11-01'), billEndDate: d('2026-11-10') }),
|
|
110
|
+
'2026-12-01',
|
|
111
|
+
)
|
|
112
|
+
expect(r.periods).toEqual([['2026-11-01', '2026-11-10', 33.33]])
|
|
113
|
+
expect(r.watermark).toBe('2026-11-10')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('an end exactly on a grid boundary is a full period (no proration)', () => {
|
|
117
|
+
const r = run(item({ billStartDate: d('2026-12-01'), billEndDate: d('2026-12-31') }), '2026-12-01')
|
|
118
|
+
expect(r.periods).toEqual([['2026-12-01', '2026-12-31', 100]])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('prorate=false skips partial periods but still advances the watermark', () => {
|
|
122
|
+
const r = run(item({ prorate: false }), '2026-12-01')
|
|
123
|
+
expect(r.periods).toEqual([['2026-12-01', '2026-12-31', 100]])
|
|
124
|
+
expect(r.skipped).toEqual([
|
|
125
|
+
expect.objectContaining({ reason: 'partial-period-not-prorated' }),
|
|
126
|
+
])
|
|
127
|
+
expect(r.watermark).toBe('2026-12-31')
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('prorate=false never credits an ended item and keeps the watermark', () => {
|
|
131
|
+
const r = run(item({ prorate: false, billedToDate: d('2026-12-31'), billEndDate: d('2026-12-15') }), '2027-01-01')
|
|
132
|
+
expect(r.lines).toHaveLength(0)
|
|
133
|
+
expect(r.skipped[0].reason).toBe('ended-after-billing-no-credit')
|
|
134
|
+
expect(r.watermark).toBe('2026-12-31')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('re-running the same bill date with the new watermark yields nothing (idempotent)', () => {
|
|
138
|
+
const first = run(item(), '2026-12-01')
|
|
139
|
+
const again = run(item({ billedToDate: first.newBilledToDate }), '2026-12-01')
|
|
140
|
+
expect(again.lines).toHaveLength(0)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('switching in arrears → in advance never overlaps or gaps', () => {
|
|
144
|
+
const arrears = run(item({ chargeTiming: 'in_arrears', billStartDate: d('2026-11-01') }), '2026-12-01')
|
|
145
|
+
expect(arrears.watermark).toBe('2026-11-30')
|
|
146
|
+
const advance = run(
|
|
147
|
+
item({ chargeTiming: 'in_advance', billStartDate: d('2026-11-01'), billedToDate: arrears.newBilledToDate }),
|
|
148
|
+
'2027-01-01',
|
|
149
|
+
)
|
|
150
|
+
expect(advance.periods).toEqual([
|
|
151
|
+
['2026-12-01', '2026-12-31', 100],
|
|
152
|
+
['2027-01-01', '2027-01-31', 100],
|
|
153
|
+
])
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('switching in advance → in arrears waits until the prepaid period has passed', () => {
|
|
157
|
+
const r = run(item({ chargeTiming: 'in_arrears', billedToDate: d('2026-12-31') }), '2027-01-01')
|
|
158
|
+
expect(r.lines).toHaveLength(0)
|
|
159
|
+
expect(r.watermark).toBe('2026-12-31')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('handles month-end anchors, leap years and other cycles', () => {
|
|
163
|
+
expect(run(item({ billStartDate: d('2027-01-31') }), '2027-01-31').periods).toEqual([
|
|
164
|
+
['2027-01-31', '2027-02-27', 100],
|
|
165
|
+
])
|
|
166
|
+
expect(run(item({ billStartDate: d('2028-02-01') }), '2028-02-01').periods).toEqual([
|
|
167
|
+
['2028-02-01', '2028-02-29', 100],
|
|
168
|
+
])
|
|
169
|
+
expect(run(item({ billStartDate: d('2026-11-16') }), '2026-11-16', 'weekly').periods).toEqual([
|
|
170
|
+
['2026-11-16', '2026-11-22', 100],
|
|
171
|
+
])
|
|
172
|
+
expect(run(item({ billStartDate: d('2026-10-01') }), '2026-10-01', 'quarterly').periods).toEqual([
|
|
173
|
+
['2026-10-01', '2026-12-31', 100],
|
|
174
|
+
])
|
|
175
|
+
expect(run(item({ billStartDate: d('2026-01-01') }), '2026-01-01', 'annually').periods).toEqual([
|
|
176
|
+
['2026-01-01', '2026-12-31', 100],
|
|
177
|
+
])
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('skips inactive and soft-deleted items', () => {
|
|
181
|
+
expect(run(item({ isActive: false }), '2026-12-01').skipped[0].reason).toBe('item-inactive')
|
|
182
|
+
expect(run(item({ deletedAt: d('2026-11-20') }), '2026-12-01').skipped[0].reason).toBe('item-soft-deleted')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('caps runaway backdating', () => {
|
|
186
|
+
const r = run(item({ chargeTiming: 'in_arrears', billStartDate: d('2000-01-01') }), '2026-12-01')
|
|
187
|
+
expect(r.lines).toHaveLength(MAX_PERIODS_PER_ITEM)
|
|
188
|
+
expect(r.skipped.at(-1)?.reason).toBe('period-cap-reached')
|
|
189
|
+
})
|
|
190
|
+
})
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { captureTopup } from '../../lib/topupCapture'
|
|
2
|
-
import { __resetInvoiceStatusCacheForTests } from '../../lib/invoiceStatus'
|
|
3
2
|
import { createPrepaidMockEm } from '../helpers/prepaidMockEm'
|
|
4
3
|
|
|
5
4
|
const ACCOUNT = {
|
|
@@ -9,22 +8,6 @@ const ACCOUNT = {
|
|
|
9
8
|
currencyCode: 'EUR',
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
function makeDeps() {
|
|
13
|
-
return {
|
|
14
|
-
numberGenerator: {
|
|
15
|
-
generate: jest.fn(async () => ({ number: 'FV/2026/06/0042', format: 'x', sequence: 42 })),
|
|
16
|
-
},
|
|
17
|
-
taxService: {
|
|
18
|
-
calculateUnitAmounts: jest.fn(async (input: { amount: number }) => ({
|
|
19
|
-
netAmount: input.amount,
|
|
20
|
-
grossAmount: input.amount,
|
|
21
|
-
taxAmount: 0,
|
|
22
|
-
taxRate: 0,
|
|
23
|
-
})),
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
11
|
function makeTopup(overrides: Record<string, unknown> = {}) {
|
|
29
12
|
return {
|
|
30
13
|
id: 'topup-1',
|
|
@@ -39,10 +22,6 @@ function makeTopup(overrides: Record<string, unknown> = {}) {
|
|
|
39
22
|
}
|
|
40
23
|
}
|
|
41
24
|
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
__resetInvoiceStatusCacheForTests()
|
|
44
|
-
})
|
|
45
|
-
|
|
46
25
|
describe('captureTopup', () => {
|
|
47
26
|
it('credits the balance, issues a receipt, and flips the top-up to captured', async () => {
|
|
48
27
|
const topup = makeTopup()
|
|
@@ -50,7 +29,7 @@ describe('captureTopup', () => {
|
|
|
50
29
|
topup,
|
|
51
30
|
balance: { tenantId: 'ten-1', billAccountId: 'acct-1', balance: '0.0000' },
|
|
52
31
|
})
|
|
53
|
-
const result = await captureTopup(em as never,
|
|
32
|
+
const result = await captureTopup(em as never, {
|
|
54
33
|
topup: topup as never,
|
|
55
34
|
account: ACCOUNT,
|
|
56
35
|
taxRate: 0,
|
|
@@ -61,7 +40,7 @@ describe('captureTopup', () => {
|
|
|
61
40
|
expect(result.balanceAfter).toBe('100.0000')
|
|
62
41
|
expect(result.transactionId).toBeTruthy()
|
|
63
42
|
expect(result.receiptInvoiceId).toBeTruthy()
|
|
64
|
-
expect(result.invoiceNumber).
|
|
43
|
+
expect(result.invoiceNumber).toMatch(/^RC\/\d{4}\/\d{2}\/0042$/)
|
|
65
44
|
|
|
66
45
|
// The credit transaction carries the deterministic source_ref.
|
|
67
46
|
expect(em.transactions).toHaveLength(1)
|
|
@@ -81,7 +60,7 @@ describe('captureTopup', () => {
|
|
|
81
60
|
topup,
|
|
82
61
|
balance: { tenantId: 'ten-1', billAccountId: 'acct-1', balance: '100.0000' },
|
|
83
62
|
})
|
|
84
|
-
const result = await captureTopup(em as never,
|
|
63
|
+
const result = await captureTopup(em as never, {
|
|
85
64
|
topup: topup as never,
|
|
86
65
|
account: ACCOUNT,
|
|
87
66
|
taxRate: 0,
|
|
@@ -107,8 +86,7 @@ describe('captureTopup', () => {
|
|
|
107
86
|
billAccountId: 'acct-1',
|
|
108
87
|
createdAt: new Date('2026-06-10'),
|
|
109
88
|
})
|
|
110
|
-
const
|
|
111
|
-
const result = await captureTopup(em as never, deps, {
|
|
89
|
+
const result = await captureTopup(em as never, {
|
|
112
90
|
topup: topup as never,
|
|
113
91
|
account: ACCOUNT,
|
|
114
92
|
taxRate: 0,
|
|
@@ -117,7 +95,7 @@ describe('captureTopup', () => {
|
|
|
117
95
|
expect(result.balanceAfter).toBe('100.0000') // unchanged — dedup hit
|
|
118
96
|
expect(result.receiptInvoiceId).toBe('inv-prior') // returns the existing receipt
|
|
119
97
|
expect(em.transactions).toHaveLength(1) // no new credit transaction
|
|
120
|
-
// No second fiscal document:
|
|
121
|
-
expect(
|
|
98
|
+
// No second fiscal document: no receipt number was claimed.
|
|
99
|
+
expect(em.sequenceClaims).toEqual([])
|
|
122
100
|
})
|
|
123
101
|
})
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { createTopupReceipt
|
|
2
|
-
import { __resetInvoiceStatusCacheForTests } from '../../lib/invoiceStatus'
|
|
1
|
+
import { createTopupReceipt } from '../../lib/topupReceipt'
|
|
3
2
|
import { createPrepaidMockEm } from '../helpers/prepaidMockEm'
|
|
4
3
|
|
|
5
4
|
const ACCOUNT = {
|
|
@@ -9,40 +8,12 @@ const ACCOUNT = {
|
|
|
9
8
|
currencyCode: 'EUR',
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
numberGenerator: {
|
|
15
|
-
generate: jest.fn(async () => ({
|
|
16
|
-
number: 'FV/2026/06/0042',
|
|
17
|
-
format: 'FV/{yyyy}/{mm}/{seq:4}',
|
|
18
|
-
sequence: 42,
|
|
19
|
-
})),
|
|
20
|
-
},
|
|
21
|
-
taxService: {
|
|
22
|
-
calculateUnitAmounts: jest.fn(async (input: { amount: number; taxRate?: number | string | null }) => {
|
|
23
|
-
const rate = typeof input.taxRate === 'number' ? input.taxRate : Number(input.taxRate ?? 0)
|
|
24
|
-
const gross = input.amount
|
|
25
|
-
const net = rate > 0 ? gross / (1 + rate / 100) : gross
|
|
26
|
-
return {
|
|
27
|
-
netAmount: Math.round(net * 10000) / 10000,
|
|
28
|
-
grossAmount: Math.round(gross * 10000) / 10000,
|
|
29
|
-
taxAmount: Math.round((gross - net) * 10000) / 10000,
|
|
30
|
-
taxRate: taxRateOut,
|
|
31
|
-
}
|
|
32
|
-
}),
|
|
33
|
-
},
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
__resetInvoiceStatusCacheForTests()
|
|
39
|
-
})
|
|
11
|
+
const RECEIPT_NUMBER = /^RC\/\d{4}\/\d{2}\/0042$/
|
|
40
12
|
|
|
41
13
|
describe('createTopupReceipt — VAT at top-up (amount is gross)', () => {
|
|
42
|
-
it('splits 100 gross @ 23% into net + VAT and
|
|
14
|
+
it('splits 100 gross @ 23% into net + VAT and issues a paid receipt document', async () => {
|
|
43
15
|
const em = createPrepaidMockEm()
|
|
44
|
-
const
|
|
45
|
-
const receipt = await createTopupReceipt(em as never, deps, {
|
|
16
|
+
const receipt = await createTopupReceipt(em as never, {
|
|
46
17
|
account: ACCOUNT,
|
|
47
18
|
topupId: 'topup-1',
|
|
48
19
|
paymentId: 'pay-1',
|
|
@@ -50,35 +21,33 @@ describe('createTopupReceipt — VAT at top-up (amount is gross)', () => {
|
|
|
50
21
|
taxRate: 23,
|
|
51
22
|
})
|
|
52
23
|
|
|
53
|
-
expect(receipt.invoiceNumber).
|
|
54
|
-
expect(receipt
|
|
55
|
-
expect(
|
|
56
|
-
expect(receipt.taxAmount).toBe('18.6992')
|
|
57
|
-
expect(receipt.taxRate).toBe(23)
|
|
24
|
+
expect(receipt.invoiceNumber).toMatch(RECEIPT_NUMBER)
|
|
25
|
+
expect(receipt).toMatchObject({ grossAmount: '100.0000', netAmount: '81.3000', taxAmount: '18.7000', taxRate: 23 })
|
|
26
|
+
expect(em.sequenceClaims).toEqual(['receipt'])
|
|
58
27
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
28
|
+
const doc = em.persisted.find((e) => (e as { documentType?: string }).documentType === 'receipt') as Record<string, unknown>
|
|
29
|
+
expect(doc).toMatchObject({
|
|
30
|
+
status: 'paid',
|
|
31
|
+
series: 'receipt',
|
|
32
|
+
topupId: 'topup-1',
|
|
33
|
+
billAccountId: 'acct-1',
|
|
34
|
+
paidTotal: '100.0000',
|
|
35
|
+
outstanding: '0',
|
|
36
|
+
totalGross: '100.0000',
|
|
37
|
+
})
|
|
38
|
+
const line = em.persisted.find((e) => (e as { billingType?: string }).billingType === 'receipt') as Record<string, unknown>
|
|
39
|
+
expect(line).toMatchObject({ invoiceId: doc.id, taxRate: '23.0000', netAmount: '81.3000', taxAmount: '18.7000' })
|
|
68
40
|
})
|
|
69
41
|
|
|
70
42
|
it('0% VAT → net == gross, tax 0', async () => {
|
|
71
43
|
const em = createPrepaidMockEm()
|
|
72
|
-
const
|
|
73
|
-
const receipt = await createTopupReceipt(em as never, deps, {
|
|
44
|
+
const receipt = await createTopupReceipt(em as never, {
|
|
74
45
|
account: ACCOUNT,
|
|
75
46
|
topupId: 'topup-2',
|
|
76
47
|
paymentId: 'pay-2',
|
|
77
48
|
amount: '50.0000',
|
|
78
49
|
taxRate: 0,
|
|
79
50
|
})
|
|
80
|
-
expect(receipt
|
|
81
|
-
expect(receipt.grossAmount).toBe('50.0000')
|
|
82
|
-
expect(receipt.taxAmount).toBe('0.0000')
|
|
51
|
+
expect(receipt).toMatchObject({ netAmount: '50.0000', grossAmount: '50.0000', taxAmount: '0.0000', taxRate: 0 })
|
|
83
52
|
})
|
|
84
53
|
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { DefaultTaxCalculationService } from '@open-mercato/core/modules/sales/services/taxCalculationService'
|
|
2
|
+
import { lineAmountsFromGross, lineAmountsFromNet, money4, round2, sumTotals } from '../../lib/vat'
|
|
3
|
+
|
|
4
|
+
describe('vat', () => {
|
|
5
|
+
it('rounds half-up symmetrically', () => {
|
|
6
|
+
expect(round2(1.005)).toBe(1.01)
|
|
7
|
+
expect(round2(-1.005)).toBe(-1.01)
|
|
8
|
+
expect(round2(2.675)).toBe(2.68)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('computes from net with net + tax = gross', () => {
|
|
12
|
+
expect(lineAmountsFromNet(100, 23)).toEqual({ net: 100, tax: 23, gross: 123 })
|
|
13
|
+
expect(lineAmountsFromNet(56.67, 23)).toEqual({ net: 56.67, tax: 13.03, gross: 69.7 })
|
|
14
|
+
expect(lineAmountsFromNet(10, 0)).toEqual({ net: 10, tax: 0, gross: 10 })
|
|
15
|
+
expect(lineAmountsFromNet(-51.61, 23)).toEqual({ net: -51.61, tax: -11.87, gross: -63.48 })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('splits gross with net + tax = gross exactly', () => {
|
|
19
|
+
for (const [gross, rate] of [[100, 23], [99.99, 8], [0.01, 23], [123, 5], [50, 0]] as const) {
|
|
20
|
+
const r = lineAmountsFromGross(gross, rate)
|
|
21
|
+
expect(round2(r.net + r.tax)).toBe(r.gross)
|
|
22
|
+
expect(r.gross).toBe(round2(gross))
|
|
23
|
+
}
|
|
24
|
+
expect(lineAmountsFromGross(123, 23)).toEqual({ net: 100, tax: 23, gross: 123 })
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('sums rounded lines into 4dp column strings', () => {
|
|
28
|
+
expect(sumTotals([lineAmountsFromNet(100, 23), lineAmountsFromNet(56.67, 23)])).toEqual({
|
|
29
|
+
subtotalNet: '156.6700',
|
|
30
|
+
taxTotal: '36.0300',
|
|
31
|
+
totalGross: '192.7000',
|
|
32
|
+
})
|
|
33
|
+
expect(money4(Number.NaN)).toBe('0.0000')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('stays within half a cent of the core tax service (gross split parity)', async () => {
|
|
37
|
+
const core = new DefaultTaxCalculationService({} as never)
|
|
38
|
+
for (const [gross, rate] of [[100, 23], [99.99, 8], [250, 5], [1234.56, 23], [0.99, 23]] as const) {
|
|
39
|
+
const ours = lineAmountsFromGross(gross, rate)
|
|
40
|
+
const theirs = await core.calculateUnitAmounts({
|
|
41
|
+
amount: gross,
|
|
42
|
+
mode: 'gross',
|
|
43
|
+
organizationId: 'o',
|
|
44
|
+
tenantId: 't',
|
|
45
|
+
taxRate: rate,
|
|
46
|
+
})
|
|
47
|
+
expect(Math.abs(ours.net - theirs.netAmount)).toBeLessThanOrEqual(0.005)
|
|
48
|
+
expect(Math.abs(ours.tax - theirs.taxAmount)).toBeLessThanOrEqual(0.005)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
})
|