@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
|
@@ -8,43 +8,97 @@ import {
|
|
|
8
8
|
BillingRun,
|
|
9
9
|
} from '../data/entities'
|
|
10
10
|
import type { BillCycle } from '../data/validators'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
addDays,
|
|
13
|
+
advanceNextBillDate,
|
|
14
|
+
deriveBillPeriod,
|
|
15
|
+
isCycleDue,
|
|
16
|
+
shiftCycles,
|
|
17
|
+
type BillPeriod,
|
|
18
|
+
} from './billPeriod'
|
|
12
19
|
import { applyBalanceMovement } from './balanceLedger'
|
|
13
20
|
import { resolveLowBalanceThreshold } from './balanceStatus'
|
|
14
|
-
import {
|
|
15
|
-
|
|
21
|
+
import {
|
|
22
|
+
buildInvoiceLinesFromItems,
|
|
23
|
+
buildRecurringLineDescriptors,
|
|
24
|
+
type InvoiceLineDescriptor,
|
|
25
|
+
} from './invoiceWriter'
|
|
26
|
+
import { shouldIncludeItem, type SelectableItem } from './itemSelector'
|
|
27
|
+
import { computeRecurringLines, type RecurringSkip } from './servicePeriods'
|
|
16
28
|
import { absMoney, addMoney, fromUnits, subMoney, toUnits } from './money'
|
|
17
29
|
import {
|
|
18
30
|
getLowBalanceThresholdDefault,
|
|
19
31
|
getNearLimitBufferDefault,
|
|
32
|
+
getPrepaidTaxPoint,
|
|
20
33
|
getStatementEnabled,
|
|
21
34
|
} from './prepaidConfig'
|
|
35
|
+
import {
|
|
36
|
+
buildVatSummary,
|
|
37
|
+
resolvePrepaidDebit,
|
|
38
|
+
type PrepaidDebit,
|
|
39
|
+
type PrepaidTaxPoint,
|
|
40
|
+
type VatSummaryRow,
|
|
41
|
+
} from './prepaidTaxPoint'
|
|
22
42
|
import { buildBalanceCrossingEvents, type DomainEvent } from './balanceEvents'
|
|
43
|
+
import { toIsoDate, toUtcMidnight } from './dateOnly'
|
|
23
44
|
|
|
24
45
|
/**
|
|
25
|
-
* Prepaid period close (SPEC-002 P3).
|
|
46
|
+
* Prepaid period close (SPEC-002 P3, reworked by Spec F).
|
|
47
|
+
*
|
|
48
|
+
* A prepaid account never gets a payable draft invoice. For every due bill
|
|
49
|
+
* date D (catch-up up to `asOfDate`, one rule for every trigger) the run:
|
|
50
|
+
* 1. charges `recurring` items per their `charge_timing` and `prorate`
|
|
51
|
+
* (Spec F, F-D4 — the same `computeRecurringLines` the postpaid engine
|
|
52
|
+
* uses: in advance → `[D, D + cycle − 1]`, in arrears → `[D − cycle,
|
|
53
|
+
* D − 1]`) and `one_time` items on the first bill date whose window
|
|
54
|
+
* overlaps them, debiting the balance through the org's tax point
|
|
55
|
+
* (Spec F, F-D3 — `resolvePrepaidDebit`);
|
|
56
|
+
* 2. builds a non-fiscal **BillingStatement** for `[D − cycle, D − 1]`
|
|
57
|
+
* (opening, top-ups, usage, charges taken at D, closing, per-line
|
|
58
|
+
* breakdown, VAT summary).
|
|
26
59
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* 1. charges its `recurring` / `one_time` items to the **balance** (atomic
|
|
30
|
-
* debit + transaction, deterministic `source_ref =
|
|
31
|
-
* recurring-{itemId}-{periodEndISO}`, then `billed_to_date = period_end`),
|
|
32
|
-
* 2. builds a non-fiscal **BillingStatement** (opening, topups, usage,
|
|
33
|
-
* recurring, closing + per-item breakdown).
|
|
60
|
+
* Metered usage was rated + debited in real time during the period — it is
|
|
61
|
+
* NOT re-rated here; the statement only summarises it.
|
|
34
62
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
63
|
+
* Bill date and item watermarks advance in memory, so test and dry runs
|
|
64
|
+
* simulate every due cycle exactly like a real run; only persistence differs.
|
|
37
65
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
66
|
+
* Anti-duplicate guards: the per-item `billed_to_date` watermark, the
|
|
67
|
+
* deterministic charge `source_ref`, and the statement unique
|
|
68
|
+
* `(bill_account_id, period_start, period_end)`. A statement that already
|
|
69
|
+
* exists for a period means that period was closed: the run skips the
|
|
70
|
+
* statement, still applies (idempotent) charges, and advances — it never
|
|
71
|
+
* leaves the account stuck behind an existing statement.
|
|
41
72
|
*/
|
|
42
73
|
|
|
43
74
|
export type PrepaidCloseMode = 'real' | 'test' | 'dry'
|
|
44
75
|
|
|
76
|
+
export type PrepaidCycleStatus = 'charged' | 'previewed' | 'empty' | 'skipped_existing'
|
|
77
|
+
|
|
78
|
+
/** Same shape as the postpaid `CycleRecord` (Spec F, P-D6) plus prepaid fields. */
|
|
79
|
+
export type PrepaidCycleRecord = {
|
|
80
|
+
bill_date: string
|
|
81
|
+
period_start: string
|
|
82
|
+
period_end: string
|
|
83
|
+
status: PrepaidCycleStatus
|
|
84
|
+
line_count: number
|
|
85
|
+
total_net: string
|
|
86
|
+
/** What left the balance for this bill date (gross in `consumption` mode). */
|
|
87
|
+
total_debit: string
|
|
88
|
+
invoice_id: null
|
|
89
|
+
statement_id: string | null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type PrepaidSkippedItem = {
|
|
93
|
+
item_id: string
|
|
94
|
+
reason: string
|
|
95
|
+
service_period_start: string | null
|
|
96
|
+
service_period_end: string | null
|
|
97
|
+
}
|
|
98
|
+
|
|
45
99
|
export type PrepaidAccountWarnings = {
|
|
46
|
-
|
|
47
|
-
|
|
100
|
+
skipped_items?: PrepaidSkippedItem[]
|
|
101
|
+
catch_up_cap_reached?: boolean
|
|
48
102
|
}
|
|
49
103
|
|
|
50
104
|
export type PrepaidAccountResult =
|
|
@@ -53,279 +107,362 @@ export type PrepaidAccountResult =
|
|
|
53
107
|
warnings: PrepaidAccountWarnings | null
|
|
54
108
|
statementId: string | null
|
|
55
109
|
cyclesEmitted: number
|
|
110
|
+
cycles: PrepaidCycleRecord[]
|
|
56
111
|
statementEvents: DomainEvent[]
|
|
57
112
|
}
|
|
58
113
|
| { status: 'failed'; errorMessage: string }
|
|
59
114
|
|
|
60
|
-
const SCHEDULED_CYCLE_LIMIT = 1
|
|
61
115
|
const CATCH_UP_HARD_CAP = 60
|
|
116
|
+
const SKIPPED_ITEMS_WARNING_CAP = 200
|
|
117
|
+
/** Skip reasons that are normal state, not worth surfacing on the outcome. */
|
|
118
|
+
const QUIET_SKIP_REASONS = new Set(['item-inactive', 'item-soft-deleted'])
|
|
62
119
|
|
|
63
120
|
export type ProcessPrepaidParams = {
|
|
64
121
|
asOfDate: Date
|
|
65
122
|
mode: PrepaidCloseMode
|
|
66
123
|
triggeredBy: 'schedule' | 'manual'
|
|
67
|
-
catchUp?: boolean
|
|
68
124
|
}
|
|
69
125
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
126
|
+
type ChargeLine = {
|
|
127
|
+
descriptor: InvoiceLineDescriptor
|
|
128
|
+
amounts: PrepaidDebit
|
|
129
|
+
sourceRef: string
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type CycleContext = {
|
|
133
|
+
mode: PrepaidCloseMode
|
|
134
|
+
taxPoint: PrepaidTaxPoint
|
|
135
|
+
statementEnabled: boolean
|
|
136
|
+
threshold: string
|
|
137
|
+
nearLimitBuffer: string
|
|
138
|
+
creditLimit: string
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function chargeSourceRef(line: InvoiceLineDescriptor, kind: 'charge' | 'credit'): string {
|
|
142
|
+
if (line.billingType === 'one_time') return `one_time-${line.billingItemId}`
|
|
143
|
+
const start = line.servicePeriodStart ? toIsoDate(line.servicePeriodStart) : 'na'
|
|
144
|
+
const end = line.servicePeriodEnd ? toIsoDate(line.servicePeriodEnd) : 'na'
|
|
145
|
+
return `recurring-${line.billingItemId}-${kind}-${start}-${end}`
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function money4(value: number): string {
|
|
149
|
+
return fromUnits(Math.round(value * 10000))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function toSkipWarning(skip: RecurringSkip): PrepaidSkippedItem {
|
|
153
|
+
return {
|
|
154
|
+
item_id: skip.billingItemId,
|
|
155
|
+
reason: skip.reason,
|
|
156
|
+
service_period_start: skip.servicePeriodStart ? toIsoDate(skip.servicePeriodStart) : null,
|
|
157
|
+
service_period_end: skip.servicePeriodEnd ? toIsoDate(skip.servicePeriodEnd) : null,
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function toCharge(descriptor: InvoiceLineDescriptor, kind: 'charge' | 'credit', taxPoint: PrepaidTaxPoint): ChargeLine {
|
|
162
|
+
return {
|
|
163
|
+
descriptor,
|
|
164
|
+
amounts: resolvePrepaidDebit({
|
|
165
|
+
lineAmount: descriptor.lineAmount,
|
|
166
|
+
priceBasis: descriptor.priceBasis,
|
|
167
|
+
taxRate: descriptor.taxRate,
|
|
168
|
+
taxPoint,
|
|
169
|
+
}),
|
|
170
|
+
sourceRef: chargeSourceRef(descriptor, kind),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
75
174
|
export async function processPrepaidAccount(
|
|
76
175
|
em: EntityManager,
|
|
77
176
|
account: BillingAccount,
|
|
78
177
|
billRun: BillingRun,
|
|
79
178
|
params: ProcessPrepaidParams,
|
|
80
179
|
): Promise<PrepaidAccountResult> {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
? SCHEDULED_CYCLE_LIMIT
|
|
84
|
-
: CATCH_UP_HARD_CAP
|
|
85
|
-
|
|
86
|
-
const statementEnabled = await getStatementEnabled(em)
|
|
87
|
-
// Resolve the crossing thresholds once — they don't change during the run.
|
|
180
|
+
const scope = { tenantId: account.tenantId, organizationId: account.organizationId }
|
|
181
|
+
const cycle = account.billCycle as BillCycle
|
|
88
182
|
const balanceRow = await em.findOne(BillingAccountBalance, {
|
|
89
183
|
tenantId: account.tenantId,
|
|
90
184
|
billAccountId: account.id,
|
|
91
185
|
})
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
await
|
|
186
|
+
const ctx: CycleContext = {
|
|
187
|
+
mode: params.mode,
|
|
188
|
+
taxPoint: await getPrepaidTaxPoint(em, scope),
|
|
189
|
+
statementEnabled: await getStatementEnabled(em, scope),
|
|
190
|
+
threshold: resolveLowBalanceThreshold(
|
|
191
|
+
balanceRow?.lowBalanceThreshold ?? null,
|
|
192
|
+
await getLowBalanceThresholdDefault(em, scope),
|
|
193
|
+
),
|
|
194
|
+
nearLimitBuffer: await getNearLimitBufferDefault(em, scope),
|
|
195
|
+
creditLimit: account.creditLimit ?? '0',
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const items = (await em.find(BillingItem, {
|
|
199
|
+
tenantId: account.tenantId,
|
|
200
|
+
organizationId: account.organizationId,
|
|
201
|
+
billAccountId: account.id,
|
|
202
|
+
deletedAt: null,
|
|
203
|
+
} as never)) as BillingItem[]
|
|
204
|
+
const recurringItems = items.filter((it) => it.type === 'recurring')
|
|
205
|
+
const oneTimeItems = items.filter((it) => it.type === 'one_time')
|
|
206
|
+
|
|
207
|
+
// In-memory cycle state — persisted per cycle only in real mode.
|
|
208
|
+
const watermarks = new Map<string, Date | null>(
|
|
209
|
+
recurringItems.map((it) => [it.id, it.billedToDate ? toUtcMidnight(it.billedToDate) : null]),
|
|
95
210
|
)
|
|
96
|
-
const
|
|
97
|
-
|
|
211
|
+
const billedOneTimes = new Set<string>(oneTimeItems.filter((it) => it.billedToDate).map((it) => it.id))
|
|
212
|
+
let billDate = toUtcMidnight(account.nextBillDate)
|
|
213
|
+
// Chain opening → closing across cycles. Null on the first cycle →
|
|
214
|
+
// resolved from the prior statement / ledger.
|
|
215
|
+
let previousClosing: string | null = null
|
|
98
216
|
|
|
99
|
-
|
|
100
|
-
|
|
217
|
+
const cycles: PrepaidCycleRecord[] = []
|
|
218
|
+
const skippedItems: PrepaidSkippedItem[] = []
|
|
219
|
+
const warnings: PrepaidAccountWarnings = {}
|
|
101
220
|
const statementEvents: DomainEvent[] = []
|
|
102
|
-
|
|
103
|
-
// cycle → closePrepaidPeriod resolves it from the prior statement / ledger.
|
|
104
|
-
let previousClosing: string | null = null
|
|
221
|
+
let lastStatementId: string | null = null
|
|
105
222
|
|
|
106
|
-
|
|
107
|
-
|
|
223
|
+
const pushSkips = (skips: readonly RecurringSkip[]) => {
|
|
224
|
+
for (const skip of skips) {
|
|
225
|
+
if (QUIET_SKIP_REASONS.has(skip.reason)) continue
|
|
226
|
+
if (skippedItems.length < SKIPPED_ITEMS_WARNING_CAP) skippedItems.push(toSkipWarning(skip))
|
|
227
|
+
}
|
|
228
|
+
}
|
|
108
229
|
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
billAccountId: account.id,
|
|
114
|
-
periodStart: period.periodStart,
|
|
115
|
-
periodEnd: period.periodEnd,
|
|
116
|
-
})
|
|
117
|
-
if (existing) {
|
|
118
|
-
return {
|
|
119
|
-
status: 'skipped_existing_statement',
|
|
120
|
-
warnings: null,
|
|
121
|
-
statementId: existing.id,
|
|
122
|
-
cyclesEmitted,
|
|
123
|
-
statementEvents,
|
|
124
|
-
}
|
|
125
|
-
}
|
|
230
|
+
while (isCycleDue(billDate, params.asOfDate)) {
|
|
231
|
+
if (cycles.length >= CATCH_UP_HARD_CAP) {
|
|
232
|
+
warnings.catch_up_cap_reached = true
|
|
233
|
+
break
|
|
126
234
|
}
|
|
235
|
+
const nextBillDate = advanceNextBillDate(billDate, cycle)
|
|
236
|
+
const period = deriveBillPeriod(billDate, cycle)
|
|
127
237
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
lastStatementId = cycle.statementId
|
|
146
|
-
statementEvents.push({
|
|
147
|
-
id: 'billing.statement.generated',
|
|
148
|
-
payload: {
|
|
149
|
-
statementId: cycle.statementId,
|
|
150
|
-
billAccountId: account.id,
|
|
151
|
-
tenantId: account.tenantId,
|
|
152
|
-
organizationId: account.organizationId,
|
|
153
|
-
billRunId: billRun.id,
|
|
154
|
-
periodStart: period.periodStart.toISOString().slice(0, 10),
|
|
155
|
-
periodEnd: period.periodEnd.toISOString().slice(0, 10),
|
|
156
|
-
currencyCode: account.currencyCode,
|
|
157
|
-
openingBalance: cycle.openingBalance,
|
|
158
|
-
closingBalance: cycle.closingBalance,
|
|
159
|
-
totalTopups: cycle.totalTopups,
|
|
160
|
-
totalUsage: cycle.totalUsage,
|
|
161
|
-
totalRecurring: cycle.totalRecurring,
|
|
162
|
-
totalAdjustments: cycle.totalAdjustments,
|
|
238
|
+
// ── Recurring: service periods against the in-memory watermarks ──
|
|
239
|
+
const charges: ChargeLine[] = []
|
|
240
|
+
const watermarkUpdates = new Map<string, Date | null>()
|
|
241
|
+
for (const item of recurringItems) {
|
|
242
|
+
const unitPrice = (item.rateJson as { unit_price?: number } | null)?.unit_price
|
|
243
|
+
if (typeof unitPrice !== 'number') continue
|
|
244
|
+
const computed = computeRecurringLines(
|
|
245
|
+
{
|
|
246
|
+
id: item.id,
|
|
247
|
+
chargeTiming: item.chargeTiming ?? 'in_advance',
|
|
248
|
+
prorate: item.prorate ?? true,
|
|
249
|
+
billStartDate: item.billStartDate,
|
|
250
|
+
billEndDate: item.billEndDate ?? null,
|
|
251
|
+
billedToDate: watermarks.get(item.id) ?? null,
|
|
252
|
+
unitPrice,
|
|
253
|
+
isActive: item.isActive,
|
|
254
|
+
deletedAt: item.deletedAt ?? null,
|
|
163
255
|
},
|
|
256
|
+
{ billDate, cycle },
|
|
257
|
+
)
|
|
258
|
+
pushSkips(computed.skipped)
|
|
259
|
+
if (computed.newBilledToDate?.getTime() !== watermarks.get(item.id)?.getTime()) {
|
|
260
|
+
watermarkUpdates.set(item.id, computed.newBilledToDate)
|
|
261
|
+
}
|
|
262
|
+
const descriptors = buildRecurringLineDescriptors(item, computed.lines)
|
|
263
|
+
descriptors.forEach((descriptor, index) => {
|
|
264
|
+
charges.push(toCharge(descriptor, computed.lines[index]?.kind ?? 'charge', ctx.taxPoint))
|
|
164
265
|
})
|
|
165
266
|
}
|
|
166
267
|
|
|
268
|
+
// ── One-time: first bill date whose [D − cycle, D + cycle − 1] window
|
|
269
|
+
// overlaps (same rule as postpaid) ──
|
|
270
|
+
const oneTimeWindow: BillPeriod = {
|
|
271
|
+
periodStart: shiftCycles(billDate, cycle, -1),
|
|
272
|
+
periodEnd: addDays(nextBillDate, -1),
|
|
273
|
+
}
|
|
274
|
+
const oneTimesThisCycle = oneTimeItems.filter(
|
|
275
|
+
(it) => !billedOneTimes.has(it.id) && shouldIncludeItem(it as SelectableItem, oneTimeWindow).include,
|
|
276
|
+
)
|
|
277
|
+
for (const descriptor of buildInvoiceLinesFromItems(oneTimesThisCycle)) {
|
|
278
|
+
charges.push(toCharge(descriptor, 'charge', ctx.taxPoint))
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const closed = await closePrepaidPeriod(em, account, billRun, { billDate, period, charges, previousClosing }, ctx)
|
|
282
|
+
previousClosing = closed.closingBalance
|
|
283
|
+
statementEvents.push(...closed.events)
|
|
284
|
+
if (closed.statementId) lastStatementId = closed.statementId
|
|
285
|
+
cycles.push(closed.record)
|
|
286
|
+
|
|
287
|
+
// Advance the in-memory state (every mode); persist in real mode only.
|
|
288
|
+
for (const [itemId, watermark] of watermarkUpdates) watermarks.set(itemId, watermark)
|
|
289
|
+
for (const item of oneTimesThisCycle) billedOneTimes.add(item.id)
|
|
167
290
|
if (params.mode === 'real') {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
291
|
+
const now = new Date()
|
|
292
|
+
for (const item of recurringItems) {
|
|
293
|
+
if (!watermarkUpdates.has(item.id)) continue
|
|
294
|
+
item.billedToDate = watermarkUpdates.get(item.id) ?? null
|
|
295
|
+
item.updatedAt = now
|
|
296
|
+
}
|
|
297
|
+
for (const item of oneTimesThisCycle) {
|
|
298
|
+
item.billedToDate = period.periodEnd
|
|
299
|
+
item.updatedAt = now
|
|
300
|
+
}
|
|
301
|
+
account.nextBillDate = nextBillDate
|
|
172
302
|
account.lastBillDate = period.periodEnd
|
|
173
|
-
account.updatedAt =
|
|
303
|
+
account.updatedAt = now
|
|
174
304
|
await em.flush()
|
|
175
|
-
} else {
|
|
176
|
-
// test / dry: do not advance state; one iteration only.
|
|
177
|
-
cyclesEmitted += 1
|
|
178
|
-
break
|
|
179
305
|
}
|
|
180
|
-
|
|
306
|
+
billDate = nextBillDate
|
|
181
307
|
}
|
|
182
308
|
|
|
309
|
+
const finalWarnings: PrepaidAccountWarnings = {
|
|
310
|
+
...warnings,
|
|
311
|
+
...(skippedItems.length ? { skipped_items: skippedItems } : {}),
|
|
312
|
+
}
|
|
183
313
|
return {
|
|
184
|
-
status: 'success',
|
|
185
|
-
warnings: null,
|
|
314
|
+
status: warnings.catch_up_cap_reached ? 'success_with_warnings' : 'success',
|
|
315
|
+
warnings: Object.keys(finalWarnings).length ? finalWarnings : null,
|
|
186
316
|
statementId: lastStatementId,
|
|
187
|
-
cyclesEmitted,
|
|
317
|
+
cyclesEmitted: cycles.filter((c) => c.status === 'charged' || c.status === 'previewed').length,
|
|
318
|
+
cycles,
|
|
188
319
|
statementEvents,
|
|
189
320
|
}
|
|
190
321
|
}
|
|
191
322
|
|
|
323
|
+
type ClosePeriodInput = {
|
|
324
|
+
billDate: Date
|
|
325
|
+
period: BillPeriod
|
|
326
|
+
charges: ChargeLine[]
|
|
327
|
+
previousClosing: string | null
|
|
328
|
+
}
|
|
329
|
+
|
|
192
330
|
type ClosePeriodResult = {
|
|
331
|
+
record: PrepaidCycleRecord
|
|
332
|
+
/** The statement CREATED by this close (null when skipped or not written). */
|
|
193
333
|
statementId: string | null
|
|
194
|
-
openingBalance: string
|
|
195
|
-
totalTopups: string
|
|
196
|
-
totalUsage: string
|
|
197
|
-
totalRecurring: string
|
|
198
|
-
totalAdjustments: string
|
|
199
334
|
closingBalance: string
|
|
200
|
-
|
|
335
|
+
events: DomainEvent[]
|
|
201
336
|
}
|
|
202
337
|
|
|
203
338
|
type DebitBreakdownEntry = {
|
|
204
339
|
billing_item_id: string
|
|
205
340
|
billing_type: string
|
|
206
|
-
|
|
341
|
+
description: string
|
|
207
342
|
quantity: string
|
|
343
|
+
/** What left the balance (kept as `amount` for older statement readers). */
|
|
208
344
|
amount: string
|
|
345
|
+
net: string
|
|
346
|
+
tax: string
|
|
347
|
+
tax_rate: string
|
|
348
|
+
service_period_start: string | null
|
|
349
|
+
service_period_end: string | null
|
|
350
|
+
proration: unknown
|
|
209
351
|
}
|
|
210
352
|
|
|
211
353
|
async function closePrepaidPeriod(
|
|
212
354
|
em: EntityManager,
|
|
213
355
|
account: BillingAccount,
|
|
214
|
-
period: BillPeriod,
|
|
215
356
|
billRun: BillingRun,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
statementEnabled: boolean
|
|
219
|
-
previousClosing: string | null
|
|
220
|
-
threshold: string
|
|
221
|
-
nearLimitBuffer: string
|
|
222
|
-
creditLimit: string
|
|
223
|
-
},
|
|
357
|
+
input: ClosePeriodInput,
|
|
358
|
+
ctx: CycleContext,
|
|
224
359
|
): Promise<ClosePeriodResult> {
|
|
225
|
-
const
|
|
360
|
+
const { period, charges } = input
|
|
361
|
+
const periodStartIso = toIsoDate(period.periodStart)
|
|
362
|
+
const periodEndIso = toIsoDate(period.periodEnd)
|
|
226
363
|
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// 2. the prior persisted statement's closing (month-over-month),
|
|
249
|
-
// 3. the ledger balance before the first-ever statement window.
|
|
250
|
-
const openingBalance = await resolveOpeningBalance(em, account, period, opts.previousClosing)
|
|
364
|
+
// Statement anti-duplicate (real only — test / dry never write statements).
|
|
365
|
+
// Date-only parameters go as ISO strings (P-D5).
|
|
366
|
+
const existingStatement =
|
|
367
|
+
ctx.mode === 'real'
|
|
368
|
+
? await em.findOne(BillingStatement, {
|
|
369
|
+
tenantId: account.tenantId,
|
|
370
|
+
billAccountId: account.id,
|
|
371
|
+
periodStart: periodStartIso,
|
|
372
|
+
periodEnd: periodEndIso,
|
|
373
|
+
} as never)
|
|
374
|
+
: null
|
|
375
|
+
|
|
376
|
+
// Ledger window of this statement: (previous bill date, this bill date] —
|
|
377
|
+
// i.e. from the day after the previous bill date up to and including the
|
|
378
|
+
// bill date D itself. Charges are taken AT D, so money that arrives on D
|
|
379
|
+
// (a top-up made on the bill date) belongs to the same statement as the
|
|
380
|
+
// charges — never an opening balance that pretends the account was short.
|
|
381
|
+
// Consecutive windows stay contiguous: [D_prev + 1, D + 1).
|
|
382
|
+
const windowStart = addDays(period.periodStart, 1)
|
|
383
|
+
const windowEnd = addDays(input.billDate, 1) // exclusive upper bound
|
|
384
|
+
const openingBalance = await resolveOpeningBalance(em, account, windowStart, period, input.previousClosing)
|
|
251
385
|
const totalTopups = await sumByTypeInWindow(em, account, 'topup', windowStart, windowEnd)
|
|
252
|
-
const totalUsage = absMoney(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// Manual adjustments / reversals that landed in the window — SIGNED (a credit
|
|
256
|
-
// is +, a debit −). Kept as a first-class term so the statement is
|
|
257
|
-
// self-consistent and the opening→closing chain never drifts.
|
|
386
|
+
const totalUsage = absMoney(await sumByTypeInWindow(em, account, 'usage', windowStart, windowEnd))
|
|
387
|
+
// Manual adjustments / reversals in the window — SIGNED. A first-class term
|
|
388
|
+
// so the statement stays self-consistent and the chain never drifts.
|
|
258
389
|
const totalAdjustments = addMoney(
|
|
259
390
|
await sumByTypeInWindow(em, account, 'adjustment', windowStart, windowEnd),
|
|
260
391
|
await sumByTypeInWindow(em, account, 'reversal', windowStart, windowEnd),
|
|
261
392
|
)
|
|
262
393
|
|
|
263
394
|
let totalRecurring = '0.0000'
|
|
395
|
+
let totalNet = '0.0000'
|
|
264
396
|
const breakdown: DebitBreakdownEntry[] = []
|
|
265
397
|
const chargeTransactionIds: string[] = []
|
|
266
|
-
|
|
267
|
-
let
|
|
268
|
-
let recurringBalanceAfter: string | null = null
|
|
398
|
+
let balanceBefore: string | null = null
|
|
399
|
+
let balanceAfter: string | null = null
|
|
269
400
|
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
},
|
|
288
|
-
})
|
|
289
|
-
if (recurringBalanceBefore === null) recurringBalanceBefore = movement.balanceBefore
|
|
290
|
-
recurringBalanceAfter = movement.balanceAfter
|
|
291
|
-
totalRecurring = addMoney(totalRecurring, line.totalNetAmount)
|
|
292
|
-
if (!movement.deduplicated) chargeTransactionIds.push(movement.transaction.id)
|
|
293
|
-
breakdown.push({
|
|
294
|
-
billing_item_id: line.billingItemId,
|
|
295
|
-
billing_type: line.billingType,
|
|
296
|
-
quantity: line.quantity,
|
|
297
|
-
amount: line.totalNetAmount,
|
|
298
|
-
})
|
|
299
|
-
const item = billableItems.find((it) => it.id === line.billingItemId)
|
|
300
|
-
if (item) {
|
|
301
|
-
item.billedToDate = period.periodEnd
|
|
302
|
-
item.updatedAt = now
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
await em.flush()
|
|
306
|
-
} else {
|
|
307
|
-
// test / dry: compute the would-be charges, persist nothing.
|
|
308
|
-
for (const line of lines) {
|
|
309
|
-
totalRecurring = addMoney(totalRecurring, line.totalNetAmount)
|
|
310
|
-
breakdown.push({
|
|
311
|
-
billing_item_id: line.billingItemId,
|
|
312
|
-
billing_type: line.billingType,
|
|
313
|
-
quantity: line.quantity,
|
|
314
|
-
amount: line.totalNetAmount,
|
|
315
|
-
})
|
|
401
|
+
for (const charge of charges) {
|
|
402
|
+
const { descriptor, amounts } = charge
|
|
403
|
+
const debit = money4(amounts.debit)
|
|
404
|
+
totalRecurring = addMoney(totalRecurring, debit)
|
|
405
|
+
totalNet = addMoney(totalNet, money4(amounts.net))
|
|
406
|
+
const metadata = {
|
|
407
|
+
bill_date: toIsoDate(input.billDate),
|
|
408
|
+
bill_period_start: periodStartIso,
|
|
409
|
+
bill_period_end: periodEndIso,
|
|
410
|
+
bill_run_id: billRun.id,
|
|
411
|
+
service_period_start: descriptor.servicePeriodStart ? toIsoDate(descriptor.servicePeriodStart) : null,
|
|
412
|
+
service_period_end: descriptor.servicePeriodEnd ? toIsoDate(descriptor.servicePeriodEnd) : null,
|
|
413
|
+
net: money4(amounts.net),
|
|
414
|
+
tax: money4(amounts.tax),
|
|
415
|
+
tax_rate: String(amounts.taxRate),
|
|
416
|
+
tax_point: ctx.taxPoint,
|
|
417
|
+
...(descriptor.metadataExtra ?? {}),
|
|
316
418
|
}
|
|
419
|
+
breakdown.push({
|
|
420
|
+
billing_item_id: descriptor.billingItemId,
|
|
421
|
+
billing_type: descriptor.billingType,
|
|
422
|
+
description: descriptor.description,
|
|
423
|
+
quantity: descriptor.quantity,
|
|
424
|
+
amount: debit,
|
|
425
|
+
net: metadata.net,
|
|
426
|
+
tax: metadata.tax,
|
|
427
|
+
tax_rate: metadata.tax_rate,
|
|
428
|
+
service_period_start: metadata.service_period_start,
|
|
429
|
+
service_period_end: metadata.service_period_end,
|
|
430
|
+
proration: (descriptor.metadataExtra as { proration?: unknown } | undefined)?.proration ?? null,
|
|
431
|
+
})
|
|
432
|
+
if (ctx.mode !== 'real') continue
|
|
433
|
+
const movement = await applyBalanceMovement(em, {
|
|
434
|
+
organizationId: account.organizationId,
|
|
435
|
+
tenantId: account.tenantId,
|
|
436
|
+
billAccountId: account.id,
|
|
437
|
+
currencyCode: account.currencyCode,
|
|
438
|
+
// Credits back (an item ended inside a charged period) stay typed as the
|
|
439
|
+
// charge they reverse: adjustments are summed by date window on
|
|
440
|
+
// statements and would be counted twice.
|
|
441
|
+
type: descriptor.billingType === 'one_time' ? 'one_time' : 'recurring',
|
|
442
|
+
amount: fromUnits(-toUnits(debit)),
|
|
443
|
+
description: descriptor.description,
|
|
444
|
+
billingItemId: descriptor.billingItemId,
|
|
445
|
+
sourceRef: charge.sourceRef,
|
|
446
|
+
metadata,
|
|
447
|
+
})
|
|
448
|
+
if (balanceBefore === null) balanceBefore = movement.balanceBefore
|
|
449
|
+
balanceAfter = movement.balanceAfter
|
|
450
|
+
if (!movement.deduplicated) chargeTransactionIds.push(movement.transaction.id)
|
|
317
451
|
}
|
|
452
|
+
if (ctx.mode === 'real' && charges.length > 0) await em.flush()
|
|
318
453
|
|
|
319
|
-
// closing = opening +
|
|
454
|
+
// closing = opening + top-ups + adjustments − usage − charges.
|
|
320
455
|
const closingBalance = subMoney(
|
|
321
456
|
subMoney(addMoney(addMoney(openingBalance, totalTopups), totalAdjustments), totalUsage),
|
|
322
457
|
totalRecurring,
|
|
323
458
|
)
|
|
459
|
+
const vatSummary: VatSummaryRow[] = buildVatSummary(
|
|
460
|
+
charges.map((c) => c.amounts),
|
|
461
|
+
ctx.taxPoint,
|
|
462
|
+
)
|
|
324
463
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const crossingEvents =
|
|
328
|
-
recurringBalanceBefore !== null && recurringBalanceAfter !== null
|
|
464
|
+
const events: DomainEvent[] =
|
|
465
|
+
balanceBefore !== null && balanceAfter !== null
|
|
329
466
|
? buildBalanceCrossingEvents({
|
|
330
467
|
scope: {
|
|
331
468
|
billAccountId: account.id,
|
|
@@ -333,16 +470,16 @@ async function closePrepaidPeriod(
|
|
|
333
470
|
organizationId: account.organizationId,
|
|
334
471
|
currencyCode: account.currencyCode,
|
|
335
472
|
},
|
|
336
|
-
balanceBefore
|
|
337
|
-
balanceAfter
|
|
338
|
-
threshold:
|
|
339
|
-
creditLimit:
|
|
340
|
-
nearLimitBuffer:
|
|
473
|
+
balanceBefore,
|
|
474
|
+
balanceAfter,
|
|
475
|
+
threshold: ctx.threshold,
|
|
476
|
+
creditLimit: ctx.creditLimit,
|
|
477
|
+
nearLimitBuffer: ctx.nearLimitBuffer,
|
|
341
478
|
})
|
|
342
479
|
: []
|
|
343
480
|
|
|
344
|
-
let
|
|
345
|
-
if (
|
|
481
|
+
let createdStatementId: string | null = null
|
|
482
|
+
if (ctx.mode === 'real' && ctx.statementEnabled && !existingStatement) {
|
|
346
483
|
const now = new Date()
|
|
347
484
|
const statement = em.create(BillingStatement, {
|
|
348
485
|
organizationId: account.organizationId,
|
|
@@ -358,45 +495,80 @@ async function closePrepaidPeriod(
|
|
|
358
495
|
totalRecurring,
|
|
359
496
|
totalAdjustments,
|
|
360
497
|
closingBalance,
|
|
361
|
-
debitBreakdown: { items: breakdown },
|
|
498
|
+
debitBreakdown: { items: breakdown, bill_date: toIsoDate(input.billDate), tax_point: ctx.taxPoint },
|
|
499
|
+
vatSummary,
|
|
362
500
|
status: 'generated',
|
|
363
501
|
createdAt: now,
|
|
364
502
|
updatedAt: now,
|
|
365
503
|
})
|
|
366
504
|
em.persist(statement)
|
|
367
505
|
await em.flush()
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
// Backfill statement_id on the charge transactions for attribution.
|
|
506
|
+
createdStatementId = statement.id
|
|
371
507
|
if (chargeTransactionIds.length > 0) {
|
|
372
508
|
await em.nativeUpdate(
|
|
373
509
|
BillingAccountTransaction,
|
|
374
510
|
{ id: { $in: chargeTransactionIds } } as never,
|
|
375
|
-
{ statementId } as never,
|
|
511
|
+
{ statementId: createdStatementId } as never,
|
|
376
512
|
)
|
|
377
513
|
}
|
|
514
|
+
events.push({
|
|
515
|
+
id: 'billing.statement.generated',
|
|
516
|
+
payload: {
|
|
517
|
+
statementId: createdStatementId,
|
|
518
|
+
billAccountId: account.id,
|
|
519
|
+
tenantId: account.tenantId,
|
|
520
|
+
organizationId: account.organizationId,
|
|
521
|
+
billRunId: billRun.id,
|
|
522
|
+
periodStart: periodStartIso,
|
|
523
|
+
periodEnd: periodEndIso,
|
|
524
|
+
currencyCode: account.currencyCode,
|
|
525
|
+
openingBalance,
|
|
526
|
+
closingBalance,
|
|
527
|
+
totalTopups,
|
|
528
|
+
totalUsage,
|
|
529
|
+
totalRecurring,
|
|
530
|
+
totalAdjustments,
|
|
531
|
+
},
|
|
532
|
+
})
|
|
378
533
|
}
|
|
379
534
|
|
|
535
|
+
let status: PrepaidCycleStatus
|
|
536
|
+
if (ctx.mode !== 'real') status = charges.length > 0 ? 'previewed' : 'empty'
|
|
537
|
+
else if (existingStatement && chargeTransactionIds.length === 0) status = 'skipped_existing'
|
|
538
|
+
else status = chargeTransactionIds.length > 0 ? 'charged' : 'empty'
|
|
539
|
+
|
|
380
540
|
return {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
541
|
+
record: {
|
|
542
|
+
bill_date: toIsoDate(input.billDate),
|
|
543
|
+
period_start: periodStartIso,
|
|
544
|
+
period_end: periodEndIso,
|
|
545
|
+
status,
|
|
546
|
+
line_count: charges.length,
|
|
547
|
+
total_net: totalNet,
|
|
548
|
+
total_debit: totalRecurring,
|
|
549
|
+
invoice_id: null,
|
|
550
|
+
statement_id: createdStatementId ?? existingStatement?.id ?? null,
|
|
551
|
+
},
|
|
552
|
+
statementId: createdStatementId,
|
|
387
553
|
closingBalance,
|
|
388
|
-
|
|
554
|
+
events,
|
|
389
555
|
}
|
|
390
556
|
}
|
|
391
557
|
|
|
392
558
|
/**
|
|
393
559
|
* Resolve a period's opening balance by chaining from the prior statement's
|
|
394
560
|
* closing (the spec's "chains cleanly"), with a ledger fallback for the very
|
|
395
|
-
* first statement.
|
|
561
|
+
* first statement. Charges are stamped at run time (after the window), so a
|
|
562
|
+
* `SUM(amount WHERE created_at < period_start)` would drop the prior
|
|
563
|
+
* period's charges and break the chain. Resolution order:
|
|
564
|
+
* 1. previous cycle's closing (catch-up, in this run),
|
|
565
|
+
* 2. the prior persisted statement's closing (month-over-month),
|
|
566
|
+
* 3. the ledger balance before the first-ever statement window.
|
|
396
567
|
*/
|
|
397
568
|
async function resolveOpeningBalance(
|
|
398
569
|
em: EntityManager,
|
|
399
570
|
account: BillingAccount,
|
|
571
|
+
windowStart: Date,
|
|
400
572
|
period: BillPeriod,
|
|
401
573
|
previousClosing: string | null,
|
|
402
574
|
): Promise<string> {
|
|
@@ -407,22 +579,18 @@ async function resolveOpeningBalance(
|
|
|
407
579
|
tenantId: account.tenantId,
|
|
408
580
|
organizationId: account.organizationId,
|
|
409
581
|
billAccountId: account.id,
|
|
410
|
-
periodEnd: { $lt: period.periodStart },
|
|
582
|
+
periodEnd: { $lt: toIsoDate(period.periodStart) },
|
|
411
583
|
} as never,
|
|
412
584
|
{ orderBy: { periodEnd: 'DESC' } },
|
|
413
585
|
)
|
|
414
586
|
if (prior) return fromUnits(toUnits(prior.closingBalance))
|
|
415
|
-
// First-ever statement: the ledger balance just before
|
|
416
|
-
//
|
|
417
|
-
return sumAmountsBefore(em, account,
|
|
587
|
+
// First-ever statement: the ledger balance just before its window (only
|
|
588
|
+
// top-ups / usage exist before the first close, so created_at is safe).
|
|
589
|
+
return sumAmountsBefore(em, account, windowStart)
|
|
418
590
|
}
|
|
419
591
|
|
|
420
592
|
/** Balance at `before` = SUM(amount) of transactions created strictly before it. */
|
|
421
|
-
async function sumAmountsBefore(
|
|
422
|
-
em: EntityManager,
|
|
423
|
-
account: BillingAccount,
|
|
424
|
-
before: Date,
|
|
425
|
-
): Promise<string> {
|
|
593
|
+
async function sumAmountsBefore(em: EntityManager, account: BillingAccount, before: Date): Promise<string> {
|
|
426
594
|
const rows = (await em.execute(
|
|
427
595
|
`SELECT COALESCE(SUM(amount), 0) AS total
|
|
428
596
|
FROM billing_account_transactions
|