@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
|
@@ -5,7 +5,10 @@ import { useRouter } from "next/navigation";
|
|
|
5
5
|
import { Play } from "lucide-react";
|
|
6
6
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
7
7
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
8
|
-
import { DataTable } from "
|
|
8
|
+
import { BillingDataTable as DataTable } from "../../../components/BillingDataTable.js";
|
|
9
|
+
import { RowActions } from "@open-mercato/ui/backend/RowActions";
|
|
10
|
+
import { BillingSettingsLink } from "../../../components/BillingSettingsLink.js";
|
|
11
|
+
import { billRunMode, useTestRunCleanup } from "../../../components/TestRunActions.js";
|
|
9
12
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
10
13
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
11
14
|
import { Input } from "@open-mercato/ui/primitives/input";
|
|
@@ -71,6 +74,7 @@ function BillRunsListPage() {
|
|
|
71
74
|
const [totalPages, setTotalPages] = React.useState(1);
|
|
72
75
|
const [loading, setLoading] = React.useState(true);
|
|
73
76
|
const [search, setSearch] = React.useState("");
|
|
77
|
+
const cleanup = useTestRunCleanup();
|
|
74
78
|
const [triggerOpen, setTriggerOpen] = React.useState(false);
|
|
75
79
|
const [triggerMode, setTriggerMode] = React.useState("real");
|
|
76
80
|
const [triggerAsOfDate, setTriggerAsOfDate] = React.useState(todayIso());
|
|
@@ -91,6 +95,16 @@ function BillRunsListPage() {
|
|
|
91
95
|
{ value: "failed", label: t("billing.runs.status.failed", "Failed") }
|
|
92
96
|
]
|
|
93
97
|
},
|
|
98
|
+
{
|
|
99
|
+
id: "mode",
|
|
100
|
+
label: t("billing.runs.filters.mode", "Mode"),
|
|
101
|
+
type: "select",
|
|
102
|
+
options: [
|
|
103
|
+
{ value: "real", label: t("billing.runs.mode.real", "Real") },
|
|
104
|
+
{ value: "test", label: t("billing.runs.mode.test", "Test") },
|
|
105
|
+
{ value: "dry", label: t("billing.runs.mode.dry_run", "Dry-run") }
|
|
106
|
+
]
|
|
107
|
+
},
|
|
94
108
|
{
|
|
95
109
|
id: "triggeredBy",
|
|
96
110
|
label: t("billing.runs.filters.triggered_by", "Trigger"),
|
|
@@ -119,6 +133,9 @@ function BillRunsListPage() {
|
|
|
119
133
|
if (typeof filters.triggeredBy === "string" && filters.triggeredBy) {
|
|
120
134
|
params.set("triggeredBy", filters.triggeredBy);
|
|
121
135
|
}
|
|
136
|
+
if (typeof filters.mode === "string" && filters.mode) {
|
|
137
|
+
params.set("mode", filters.mode);
|
|
138
|
+
}
|
|
122
139
|
const result = await readApiResultOrThrow(
|
|
123
140
|
`/api/billing/runs?${params.toString()}`
|
|
124
141
|
);
|
|
@@ -128,7 +145,7 @@ function BillRunsListPage() {
|
|
|
128
145
|
} finally {
|
|
129
146
|
setLoading(false);
|
|
130
147
|
}
|
|
131
|
-
}, [filters.status, filters.triggeredBy, page, pageSize, search]);
|
|
148
|
+
}, [filters.mode, filters.status, filters.triggeredBy, page, pageSize, search]);
|
|
132
149
|
React.useEffect(() => {
|
|
133
150
|
void loadRows();
|
|
134
151
|
}, [loadRows]);
|
|
@@ -235,9 +252,12 @@ function BillRunsListPage() {
|
|
|
235
252
|
DataTable,
|
|
236
253
|
{
|
|
237
254
|
title: t("billing.runs.title", "Bill Runs"),
|
|
238
|
-
actions: /* @__PURE__ */ jsxs(
|
|
239
|
-
/* @__PURE__ */ jsx(
|
|
240
|
-
|
|
255
|
+
actions: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
256
|
+
/* @__PURE__ */ jsx(BillingSettingsLink, {}),
|
|
257
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => setTriggerOpen(true), children: [
|
|
258
|
+
/* @__PURE__ */ jsx(Play, { size: 16 }),
|
|
259
|
+
t("billing.runs.trigger.action", "New run")
|
|
260
|
+
] })
|
|
241
261
|
] }),
|
|
242
262
|
refreshButton: {
|
|
243
263
|
label: t("billing.common.refresh", "Refresh"),
|
|
@@ -249,6 +269,36 @@ function BillRunsListPage() {
|
|
|
249
269
|
columns,
|
|
250
270
|
data: rows,
|
|
251
271
|
onRowClick: (row) => router.push(`/backend/billing/runs/${row.id}`),
|
|
272
|
+
rowActions: (row) => {
|
|
273
|
+
const mode = billRunMode(row);
|
|
274
|
+
return /* @__PURE__ */ jsx(
|
|
275
|
+
RowActions,
|
|
276
|
+
{
|
|
277
|
+
items: [
|
|
278
|
+
{
|
|
279
|
+
id: "open",
|
|
280
|
+
label: t("billing.runs.actions.open", "Open"),
|
|
281
|
+
href: `/backend/billing/runs/${row.id}`
|
|
282
|
+
},
|
|
283
|
+
...mode !== "real" && row.status !== "running" ? [
|
|
284
|
+
{
|
|
285
|
+
id: "delete",
|
|
286
|
+
label: t("billing.runs.delete.action", "Delete run"),
|
|
287
|
+
destructive: true,
|
|
288
|
+
onSelect: async () => {
|
|
289
|
+
const removed = await cleanup.remove({
|
|
290
|
+
runId: row.id,
|
|
291
|
+
mode,
|
|
292
|
+
draftsCount: row.summary?.drafts_created
|
|
293
|
+
});
|
|
294
|
+
if (removed !== null) void loadRows();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
] : []
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
},
|
|
252
302
|
isLoading: loading,
|
|
253
303
|
pagination: {
|
|
254
304
|
page,
|
|
@@ -304,7 +354,7 @@ function BillRunsListPage() {
|
|
|
304
354
|
),
|
|
305
355
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t(
|
|
306
356
|
"billing.runs.trigger.dialog.as_of_date.help",
|
|
307
|
-
"
|
|
357
|
+
"Bills every due cycle up to this date \u2014 one draft per bill date. Test and dry-run simulate them without changing accounts."
|
|
308
358
|
) })
|
|
309
359
|
] })
|
|
310
360
|
] }),
|
|
@@ -332,7 +382,8 @@ function BillRunsListPage() {
|
|
|
332
382
|
/* @__PURE__ */ jsx(Button, { type: "button", onClick: () => void handleTrigger(), disabled: triggering, children: triggering ? t("billing.runs.trigger.dialog.running", "Running\u2026") : t("billing.runs.trigger.dialog.submit", "Run now") })
|
|
333
383
|
] })
|
|
334
384
|
] })
|
|
335
|
-
] }) })
|
|
385
|
+
] }) }),
|
|
386
|
+
cleanup.ConfirmDialogElement
|
|
336
387
|
] }) });
|
|
337
388
|
}
|
|
338
389
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/billing/backend/billing/runs/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Play } from 'lucide-react'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Kbd, KbdShortcut } from '@open-mercato/ui/primitives/kbd'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { billRunName } from '../../../lib/billRunLabel'\n\n/**\n * Bill Runs admin list page.\n *\n * Lists every Bill Run for the operator's tenant with status, mode,\n * summary stats, and a link into the detail page. The \"New run\" button\n * opens an inline trigger dialog that posts to `/api/billing/runs` \u2014\n * same endpoint operators previously had to curl by hand. Retry-failed\n * stays on the run detail page (scoped to a specific run's failures).\n */\n\ntype TriggerMode = 'real' | 'dry' | 'test'\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\n// API list rows are snake_case (see `api/runs/route.ts` `fields`).\ntype BillRunRow = {\n id: string\n triggered_by: 'schedule' | 'manual'\n parent_run_id: string | null\n dry_run: boolean\n test_mode: boolean\n catch_up: boolean\n as_of_date: string\n started_at: string | null\n finished_at: string | null\n status: 'running' | 'completed' | 'partial_failure' | 'failed'\n summary: {\n accounts_processed?: number\n drafts_created?: number\n drafts_skipped_existing?: number\n accounts_failed?: number\n accounts_with_warnings?: number\n usage_records_rated?: number\n } | null\n created_at: string\n updated_at: string\n}\n\ntype ListResponse = {\n items: BillRunRow[]\n total: number\n totalPages: number\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString()\n}\n\nfunction formatDateOnly(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nfunction statusVariant(\n status: BillRunRow['status'],\n): 'default' | 'success' | 'warning' | 'error' {\n switch (status) {\n case 'completed':\n return 'success'\n case 'partial_failure':\n return 'warning'\n case 'failed':\n return 'error'\n case 'running':\n default:\n return 'default'\n }\n}\n\nfunction modeBadge(row: BillRunRow): { label: string; variant: 'default' | 'warning' } | null {\n if (row.dry_run) return { label: 'Dry-run', variant: 'warning' }\n if (row.test_mode) return { label: 'Test', variant: 'warning' }\n return null\n}\n\nexport default function BillRunsListPage() {\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<BillRunRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(25)\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const [search, setSearch] = React.useState('')\n\n // Trigger dialog \u2014 controls the New-run modal and its submit state.\n const [triggerOpen, setTriggerOpen] = React.useState(false)\n const [triggerMode, setTriggerMode] = React.useState<TriggerMode>('real')\n const [triggerAsOfDate, setTriggerAsOfDate] = React.useState<string>(todayIso())\n const [triggering, setTriggering] = React.useState(false)\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'status',\n label: t('billing.runs.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'running', label: t('billing.runs.status.running', 'Running') },\n { value: 'completed', label: t('billing.runs.status.completed', 'Completed') },\n {\n value: 'partial_failure',\n label: t('billing.runs.status.partial_failure', 'Partial failure'),\n },\n { value: 'failed', label: t('billing.runs.status.failed', 'Failed') },\n ],\n },\n {\n id: 'triggeredBy',\n label: t('billing.runs.filters.triggered_by', 'Trigger'),\n type: 'select',\n options: [\n { value: 'schedule', label: t('billing.runs.trigger.schedule', 'Schedule') },\n { value: 'manual', label: t('billing.runs.trigger.manual', 'Manual') },\n ],\n },\n ],\n [t],\n )\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(pageSize),\n sortField: 'startedAt',\n sortDir: 'desc',\n })\n if (search.trim()) params.set('search', search.trim())\n if (typeof filters.status === 'string' && filters.status) {\n params.set('status', filters.status)\n }\n if (typeof filters.triggeredBy === 'string' && filters.triggeredBy) {\n params.set('triggeredBy', filters.triggeredBy)\n }\n const result = await readApiResultOrThrow<ListResponse>(\n `/api/billing/runs?${params.toString()}`,\n )\n setRows(result.items ?? [])\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n } finally {\n setLoading(false)\n }\n }, [filters.status, filters.triggeredBy, page, pageSize, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const handleTrigger = React.useCallback(async () => {\n setTriggering(true)\n try {\n const payload: Record<string, unknown> = { mode: triggerMode }\n if (triggerAsOfDate) payload.asOfDate = triggerAsOfDate\n const call = await apiCallOrThrow<{ billRunId: string | null }>(\n '/api/billing/runs',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n )\n flash(t('billing.runs.trigger.success', 'Bill Run triggered'), 'success')\n setTriggerOpen(false)\n const newId = call.result?.billRunId\n if (newId) {\n router.push(`/backend/billing/runs/${newId}`)\n } else {\n void loadRows()\n }\n } catch (err) {\n const { message, fieldErrors } = normalizeCrudServerError(err)\n const fieldDetail =\n fieldErrors && Object.keys(fieldErrors).length\n ? Object.entries(fieldErrors)\n .map(([field, msg]) => `${field}: ${msg}`)\n .join('; ')\n : null\n const detailed =\n fieldDetail && message ? `${message} \u2014 ${fieldDetail}` : fieldDetail || message\n flash(detailed || t('billing.runs.trigger.error', 'Failed to trigger run'), 'error')\n } finally {\n setTriggering(false)\n }\n }, [loadRows, router, t, triggerAsOfDate, triggerMode])\n\n const handleTriggerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (!triggering) void handleTrigger()\n }\n },\n [handleTrigger, triggering],\n )\n\n const columns = React.useMemo<ColumnDef<BillRunRow>[]>(\n () => [\n {\n id: 'name',\n header: t('billing.runs.columns.name', 'Bill Run'),\n cell: ({ row }) => (\n <span className=\"text-sm font-medium text-primary\">\n {billRunName(row.original.as_of_date)}\n </span>\n ),\n },\n {\n accessorKey: 'status',\n header: t('billing.runs.columns.status', 'Status'),\n cell: ({ row }) => {\n const mode = modeBadge(row.original)\n return (\n <div className=\"flex items-center gap-2\">\n <Tag variant={statusVariant(row.original.status)}>\n {row.original.status}\n </Tag>\n {mode ? <Tag variant={mode.variant}>{mode.label}</Tag> : null}\n </div>\n )\n },\n },\n {\n accessorKey: 'as_of_date',\n header: t('billing.runs.columns.as_of_date', 'As-of date'),\n cell: ({ row }) => formatDateOnly(row.original.as_of_date),\n },\n {\n accessorKey: 'triggered_by',\n header: t('billing.runs.columns.triggered_by', 'Trigger'),\n },\n {\n id: 'drafts_created',\n header: t('billing.runs.columns.drafts', 'Drafts'),\n cell: ({ row }) => row.original.summary?.drafts_created ?? '\u2014',\n },\n {\n id: 'accounts_processed',\n header: t('billing.runs.columns.accounts', 'Accounts'),\n cell: ({ row }) => row.original.summary?.accounts_processed ?? '\u2014',\n },\n {\n id: 'accounts_failed',\n header: t('billing.runs.columns.failed', 'Failed'),\n cell: ({ row }) => {\n const failed = row.original.summary?.accounts_failed ?? 0\n if (failed === 0) return '\u2014'\n return <Tag variant=\"error\">{failed}</Tag>\n },\n },\n {\n accessorKey: 'started_at',\n header: t('billing.runs.columns.started_at', 'Started'),\n cell: ({ row }) => formatDate(row.original.started_at),\n },\n {\n accessorKey: 'finished_at',\n header: t('billing.runs.columns.finished_at', 'Finished'),\n cell: ({ row }) => formatDate(row.original.finished_at),\n },\n ],\n [t],\n )\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('billing.runs.title', 'Bill Runs')}\n actions={\n <Button onClick={() => setTriggerOpen(true)}>\n <Play size={16} />\n {t('billing.runs.trigger.action', 'New run')}\n </Button>\n }\n refreshButton={{\n label: t('billing.common.refresh', 'Refresh'),\n onRefresh: loadRows,\n isRefreshing: loading,\n }}\n columnChooser={{ auto: true }}\n perspective={{ tableId: 'billing-runs' }}\n columns={columns}\n data={rows}\n onRowClick={(row) => router.push(`/backend/billing/runs/${row.id}`)}\n isLoading={loading}\n pagination={{\n page,\n pageSize,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={setFilters}\n onFiltersClear={() => setFilters({})}\n searchValue={search}\n onSearchChange={setSearch}\n />\n\n <Dialog open={triggerOpen} onOpenChange={(next) => !triggering && setTriggerOpen(next)}>\n <DialogContent onKeyDown={handleTriggerKeyDown}>\n <DialogHeader>\n <DialogTitle>\n {t('billing.runs.trigger.dialog.title', 'Trigger Bill Run')}\n </DialogTitle>\n <DialogDescription>\n {t(\n 'billing.runs.trigger.dialog.description',\n 'Runs the bill cycle for every account whose next bill date has passed. Dry-run / Test produce drafts without posting any invoices.',\n )}\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-4\">\n <label className=\"flex flex-col gap-1 text-sm\">\n <span className=\"text-muted-foreground\">\n {t('billing.runs.trigger.dialog.mode', 'Mode')}\n </span>\n <Select\n value={triggerMode}\n onValueChange={(next) => setTriggerMode(next as TriggerMode)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"real\">\n {t('billing.runs.trigger.dialog.mode.real', 'Real \u2014 create draft invoices')}\n </SelectItem>\n <SelectItem value=\"dry\">\n {t('billing.runs.trigger.dialog.mode.dry', 'Dry-run \u2014 no rows written')}\n </SelectItem>\n <SelectItem value=\"test\">\n {t('billing.runs.trigger.dialog.mode.test', 'Test \u2014 drafts tagged for cleanup')}\n </SelectItem>\n </SelectContent>\n </Select>\n </label>\n <label className=\"flex flex-col gap-1 text-sm\">\n <span className=\"text-muted-foreground\">\n {t('billing.runs.trigger.dialog.as_of_date', 'As-of date')}\n </span>\n <Input\n type=\"date\"\n value={triggerAsOfDate}\n onChange={(event) => setTriggerAsOfDate(event.currentTarget.value)}\n />\n <span className=\"text-xs text-muted-foreground\">\n {t(\n 'billing.runs.trigger.dialog.as_of_date.help',\n 'Accounts whose next bill date is on or before this date will be billed.',\n )}\n </span>\n </label>\n </div>\n <DialogFooter className=\"flex items-center justify-between gap-3 sm:justify-between\">\n <span className=\"text-xs text-muted-foreground\">\n <KbdShortcut keys={['Ctrl/\u2318', 'Enter']} />{' '}\n {t('billing.runs.trigger.dialog.submit_hint', 'to run,')} <Kbd>Esc</Kbd>{' '}\n {t('billing.runs.trigger.dialog.cancel_hint', 'to cancel')}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setTriggerOpen(false)}\n disabled={triggering}\n >\n {t('billing.common.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={() => void handleTrigger()} disabled={triggering}>\n {triggering\n ? t('billing.runs.trigger.dialog.running', 'Running\u2026')\n : t('billing.runs.trigger.dialog.submit', 'Run now')}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Play } from 'lucide-react'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { BillingDataTable as DataTable } from '../../../components/BillingDataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { BillingSettingsLink } from '../../../components/BillingSettingsLink'\nimport { billRunMode, useTestRunCleanup } from '../../../components/TestRunActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Kbd, KbdShortcut } from '@open-mercato/ui/primitives/kbd'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { billRunName } from '../../../lib/billRunLabel'\n\n/**\n * Bill Runs admin list page.\n *\n * Lists every Bill Run for the operator's tenant with status, mode,\n * summary stats, and a link into the detail page. The \"New run\" button\n * opens an inline trigger dialog that posts to `/api/billing/runs` \u2014\n * same endpoint operators previously had to curl by hand. Retry-failed\n * stays on the run detail page (scoped to a specific run's failures).\n */\n\ntype TriggerMode = 'real' | 'dry' | 'test'\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\n// API list rows are snake_case (see `api/runs/route.ts` `fields`).\ntype BillRunRow = {\n id: string\n triggered_by: 'schedule' | 'manual'\n parent_run_id: string | null\n dry_run: boolean\n test_mode: boolean\n as_of_date: string\n started_at: string | null\n finished_at: string | null\n status: 'running' | 'completed' | 'partial_failure' | 'failed'\n summary: {\n accounts_processed?: number\n drafts_created?: number\n drafts_skipped_existing?: number\n accounts_failed?: number\n accounts_with_warnings?: number\n usage_records_rated?: number\n } | null\n created_at: string\n updated_at: string\n}\n\ntype ListResponse = {\n items: BillRunRow[]\n total: number\n totalPages: number\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString()\n}\n\nfunction formatDateOnly(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nfunction statusVariant(\n status: BillRunRow['status'],\n): 'default' | 'success' | 'warning' | 'error' {\n switch (status) {\n case 'completed':\n return 'success'\n case 'partial_failure':\n return 'warning'\n case 'failed':\n return 'error'\n case 'running':\n default:\n return 'default'\n }\n}\n\nfunction modeBadge(row: BillRunRow): { label: string; variant: 'default' | 'warning' } | null {\n if (row.dry_run) return { label: 'Dry-run', variant: 'warning' }\n if (row.test_mode) return { label: 'Test', variant: 'warning' }\n return null\n}\n\nexport default function BillRunsListPage() {\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<BillRunRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(25)\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const [search, setSearch] = React.useState('')\n const cleanup = useTestRunCleanup()\n\n // Trigger dialog \u2014 controls the New-run modal and its submit state.\n const [triggerOpen, setTriggerOpen] = React.useState(false)\n const [triggerMode, setTriggerMode] = React.useState<TriggerMode>('real')\n const [triggerAsOfDate, setTriggerAsOfDate] = React.useState<string>(todayIso())\n const [triggering, setTriggering] = React.useState(false)\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'status',\n label: t('billing.runs.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'running', label: t('billing.runs.status.running', 'Running') },\n { value: 'completed', label: t('billing.runs.status.completed', 'Completed') },\n {\n value: 'partial_failure',\n label: t('billing.runs.status.partial_failure', 'Partial failure'),\n },\n { value: 'failed', label: t('billing.runs.status.failed', 'Failed') },\n ],\n },\n {\n id: 'mode',\n label: t('billing.runs.filters.mode', 'Mode'),\n type: 'select',\n options: [\n { value: 'real', label: t('billing.runs.mode.real', 'Real') },\n { value: 'test', label: t('billing.runs.mode.test', 'Test') },\n { value: 'dry', label: t('billing.runs.mode.dry_run', 'Dry-run') },\n ],\n },\n {\n id: 'triggeredBy',\n label: t('billing.runs.filters.triggered_by', 'Trigger'),\n type: 'select',\n options: [\n { value: 'schedule', label: t('billing.runs.trigger.schedule', 'Schedule') },\n { value: 'manual', label: t('billing.runs.trigger.manual', 'Manual') },\n ],\n },\n ],\n [t],\n )\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(pageSize),\n sortField: 'startedAt',\n sortDir: 'desc',\n })\n if (search.trim()) params.set('search', search.trim())\n if (typeof filters.status === 'string' && filters.status) {\n params.set('status', filters.status)\n }\n if (typeof filters.triggeredBy === 'string' && filters.triggeredBy) {\n params.set('triggeredBy', filters.triggeredBy)\n }\n if (typeof filters.mode === 'string' && filters.mode) {\n params.set('mode', filters.mode)\n }\n const result = await readApiResultOrThrow<ListResponse>(\n `/api/billing/runs?${params.toString()}`,\n )\n setRows(result.items ?? [])\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n } finally {\n setLoading(false)\n }\n }, [filters.mode, filters.status, filters.triggeredBy, page, pageSize, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const handleTrigger = React.useCallback(async () => {\n setTriggering(true)\n try {\n const payload: Record<string, unknown> = { mode: triggerMode }\n if (triggerAsOfDate) payload.asOfDate = triggerAsOfDate\n const call = await apiCallOrThrow<{ billRunId: string | null }>(\n '/api/billing/runs',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n )\n flash(t('billing.runs.trigger.success', 'Bill Run triggered'), 'success')\n setTriggerOpen(false)\n const newId = call.result?.billRunId\n if (newId) {\n router.push(`/backend/billing/runs/${newId}`)\n } else {\n void loadRows()\n }\n } catch (err) {\n const { message, fieldErrors } = normalizeCrudServerError(err)\n const fieldDetail =\n fieldErrors && Object.keys(fieldErrors).length\n ? Object.entries(fieldErrors)\n .map(([field, msg]) => `${field}: ${msg}`)\n .join('; ')\n : null\n const detailed =\n fieldDetail && message ? `${message} \u2014 ${fieldDetail}` : fieldDetail || message\n flash(detailed || t('billing.runs.trigger.error', 'Failed to trigger run'), 'error')\n } finally {\n setTriggering(false)\n }\n }, [loadRows, router, t, triggerAsOfDate, triggerMode])\n\n const handleTriggerKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (!triggering) void handleTrigger()\n }\n },\n [handleTrigger, triggering],\n )\n\n const columns = React.useMemo<ColumnDef<BillRunRow>[]>(\n () => [\n {\n id: 'name',\n header: t('billing.runs.columns.name', 'Bill Run'),\n cell: ({ row }) => (\n <span className=\"text-sm font-medium text-primary\">\n {billRunName(row.original.as_of_date)}\n </span>\n ),\n },\n {\n accessorKey: 'status',\n header: t('billing.runs.columns.status', 'Status'),\n cell: ({ row }) => {\n const mode = modeBadge(row.original)\n return (\n <div className=\"flex items-center gap-2\">\n <Tag variant={statusVariant(row.original.status)}>\n {row.original.status}\n </Tag>\n {mode ? <Tag variant={mode.variant}>{mode.label}</Tag> : null}\n </div>\n )\n },\n },\n {\n accessorKey: 'as_of_date',\n header: t('billing.runs.columns.as_of_date', 'As-of date'),\n cell: ({ row }) => formatDateOnly(row.original.as_of_date),\n },\n {\n accessorKey: 'triggered_by',\n header: t('billing.runs.columns.triggered_by', 'Trigger'),\n },\n {\n id: 'drafts_created',\n header: t('billing.runs.columns.drafts', 'Drafts'),\n cell: ({ row }) => row.original.summary?.drafts_created ?? '\u2014',\n },\n {\n id: 'accounts_processed',\n header: t('billing.runs.columns.accounts', 'Accounts'),\n cell: ({ row }) => row.original.summary?.accounts_processed ?? '\u2014',\n },\n {\n id: 'accounts_failed',\n header: t('billing.runs.columns.failed', 'Failed'),\n cell: ({ row }) => {\n const failed = row.original.summary?.accounts_failed ?? 0\n if (failed === 0) return '\u2014'\n return <Tag variant=\"error\">{failed}</Tag>\n },\n },\n {\n accessorKey: 'started_at',\n header: t('billing.runs.columns.started_at', 'Started'),\n cell: ({ row }) => formatDate(row.original.started_at),\n },\n {\n accessorKey: 'finished_at',\n header: t('billing.runs.columns.finished_at', 'Finished'),\n cell: ({ row }) => formatDate(row.original.finished_at),\n },\n ],\n [t],\n )\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('billing.runs.title', 'Bill Runs')}\n actions={\n <div className=\"flex items-center gap-2\">\n <BillingSettingsLink />\n <Button onClick={() => setTriggerOpen(true)}>\n <Play size={16} />\n {t('billing.runs.trigger.action', 'New run')}\n </Button>\n </div>\n }\n refreshButton={{\n label: t('billing.common.refresh', 'Refresh'),\n onRefresh: loadRows,\n isRefreshing: loading,\n }}\n columnChooser={{ auto: true }}\n perspective={{ tableId: 'billing-runs' }}\n columns={columns}\n data={rows}\n onRowClick={(row) => router.push(`/backend/billing/runs/${row.id}`)}\n rowActions={(row) => {\n const mode = billRunMode(row)\n return (\n <RowActions\n items={[\n {\n id: 'open',\n label: t('billing.runs.actions.open', 'Open'),\n href: `/backend/billing/runs/${row.id}`,\n },\n ...(mode !== 'real' && row.status !== 'running'\n ? [\n {\n id: 'delete',\n label: t('billing.runs.delete.action', 'Delete run'),\n destructive: true,\n onSelect: async () => {\n const removed = await cleanup.remove({\n runId: row.id,\n mode,\n draftsCount: row.summary?.drafts_created,\n })\n if (removed !== null) void loadRows()\n },\n },\n ]\n : []),\n ]}\n />\n )\n }}\n isLoading={loading}\n pagination={{\n page,\n pageSize,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={setFilters}\n onFiltersClear={() => setFilters({})}\n searchValue={search}\n onSearchChange={setSearch}\n />\n\n <Dialog open={triggerOpen} onOpenChange={(next) => !triggering && setTriggerOpen(next)}>\n <DialogContent onKeyDown={handleTriggerKeyDown}>\n <DialogHeader>\n <DialogTitle>\n {t('billing.runs.trigger.dialog.title', 'Trigger Bill Run')}\n </DialogTitle>\n <DialogDescription>\n {t(\n 'billing.runs.trigger.dialog.description',\n 'Runs the bill cycle for every account whose next bill date has passed. Dry-run / Test produce drafts without posting any invoices.',\n )}\n </DialogDescription>\n </DialogHeader>\n <div className=\"space-y-4\">\n <label className=\"flex flex-col gap-1 text-sm\">\n <span className=\"text-muted-foreground\">\n {t('billing.runs.trigger.dialog.mode', 'Mode')}\n </span>\n <Select\n value={triggerMode}\n onValueChange={(next) => setTriggerMode(next as TriggerMode)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"real\">\n {t('billing.runs.trigger.dialog.mode.real', 'Real \u2014 create draft invoices')}\n </SelectItem>\n <SelectItem value=\"dry\">\n {t('billing.runs.trigger.dialog.mode.dry', 'Dry-run \u2014 no rows written')}\n </SelectItem>\n <SelectItem value=\"test\">\n {t('billing.runs.trigger.dialog.mode.test', 'Test \u2014 drafts tagged for cleanup')}\n </SelectItem>\n </SelectContent>\n </Select>\n </label>\n <label className=\"flex flex-col gap-1 text-sm\">\n <span className=\"text-muted-foreground\">\n {t('billing.runs.trigger.dialog.as_of_date', 'As-of date')}\n </span>\n <Input\n type=\"date\"\n value={triggerAsOfDate}\n onChange={(event) => setTriggerAsOfDate(event.currentTarget.value)}\n />\n <span className=\"text-xs text-muted-foreground\">\n {t(\n 'billing.runs.trigger.dialog.as_of_date.help',\n 'Bills every due cycle up to this date \u2014 one draft per bill date. Test and dry-run simulate them without changing accounts.',\n )}\n </span>\n </label>\n </div>\n <DialogFooter className=\"flex items-center justify-between gap-3 sm:justify-between\">\n <span className=\"text-xs text-muted-foreground\">\n <KbdShortcut keys={['Ctrl/\u2318', 'Enter']} />{' '}\n {t('billing.runs.trigger.dialog.submit_hint', 'to run,')} <Kbd>Esc</Kbd>{' '}\n {t('billing.runs.trigger.dialog.cancel_hint', 'to cancel')}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setTriggerOpen(false)}\n disabled={triggering}\n >\n {t('billing.common.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={() => void handleTrigger()} disabled={triggering}>\n {triggering\n ? t('billing.runs.trigger.dialog.running', 'Running\u2026')\n : t('billing.runs.trigger.dialog.submit', 'Run now')}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {cleanup.ConfirmDialogElement}\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -8,7 +8,7 @@ import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
|
8
8
|
import { FormHeader } from "@open-mercato/ui/backend/forms";
|
|
9
9
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
10
10
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
11
|
-
import { DataTable } from "
|
|
11
|
+
import { BillingDataTable as DataTable } from "../../../../components/BillingDataTable.js";
|
|
12
12
|
import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
13
13
|
import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
14
14
|
import { DetailCard, DetailField } from "../../../../components/DetailFields.js";
|
|
@@ -47,6 +47,8 @@ function BillingStatementDetailPage(props) {
|
|
|
47
47
|
};
|
|
48
48
|
}, [statementId, t]);
|
|
49
49
|
const breakdown = row?.debit_breakdown?.items ?? [];
|
|
50
|
+
const vatSummary = row?.vat_summary ?? [];
|
|
51
|
+
const showVat = vatSummary.length > 0;
|
|
50
52
|
const columns = React.useMemo(
|
|
51
53
|
() => [
|
|
52
54
|
{
|
|
@@ -57,19 +59,62 @@ function BillingStatementDetailPage(props) {
|
|
|
57
59
|
{
|
|
58
60
|
accessorKey: "billing_item_id",
|
|
59
61
|
header: t("billing.statements.breakdown.item", "Item"),
|
|
60
|
-
cell: ({ row: row2 }) => /* @__PURE__ */ jsx(
|
|
62
|
+
cell: ({ row: row2 }) => /* @__PURE__ */ jsx(Link, { href: `/backend/billing/items/${row2.original.billing_item_id}`, className: "text-sm text-primary hover:underline", children: row2.original.description ?? row2.original.billing_item_id })
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
accessorKey: "service_period_start",
|
|
66
|
+
header: t("billing.statements.breakdown.service_period", "Service period"),
|
|
67
|
+
cell: ({ row: row2 }) => row2.original.service_period_start ? `${row2.original.service_period_start} \u2013 ${row2.original.service_period_end ?? "\u2026"}` : "\u2014"
|
|
61
68
|
},
|
|
62
69
|
{
|
|
63
70
|
accessorKey: "quantity",
|
|
64
71
|
header: t("billing.statements.breakdown.quantity", "Qty"),
|
|
65
72
|
cell: ({ row: row2 }) => /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row2.original.quantity })
|
|
66
73
|
},
|
|
74
|
+
...showVat ? [
|
|
75
|
+
{
|
|
76
|
+
accessorKey: "net",
|
|
77
|
+
header: t("billing.statements.breakdown.net", "Net"),
|
|
78
|
+
cell: ({ row: row2 }) => /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row2.original.net ?? "\u2014" })
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
accessorKey: "tax",
|
|
82
|
+
header: t("billing.statements.breakdown.vat", "VAT"),
|
|
83
|
+
cell: ({ row: row2 }) => row2.original.tax !== void 0 ? /* @__PURE__ */ jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
84
|
+
row2.original.tax,
|
|
85
|
+
" (",
|
|
86
|
+
row2.original.tax_rate ?? "0",
|
|
87
|
+
" %)"
|
|
88
|
+
] }) : "\u2014"
|
|
89
|
+
}
|
|
90
|
+
] : [],
|
|
67
91
|
{
|
|
68
92
|
accessorKey: "amount",
|
|
69
|
-
header: t("billing.statements.breakdown.
|
|
93
|
+
header: t("billing.statements.breakdown.charged", "Charged to balance"),
|
|
70
94
|
cell: ({ row: row2 }) => /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row2.original.amount })
|
|
71
95
|
}
|
|
72
96
|
],
|
|
97
|
+
[showVat, t]
|
|
98
|
+
);
|
|
99
|
+
const vatColumns = React.useMemo(
|
|
100
|
+
() => [
|
|
101
|
+
{ accessorKey: "rate", header: t("billing.statements.vat.rate", "VAT rate"), cell: ({ row: row2 }) => `${row2.original.rate} %` },
|
|
102
|
+
{
|
|
103
|
+
accessorKey: "net",
|
|
104
|
+
header: t("billing.statements.breakdown.net", "Net"),
|
|
105
|
+
cell: ({ row: row2 }) => /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row2.original.net })
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
accessorKey: "tax",
|
|
109
|
+
header: t("billing.statements.breakdown.vat", "VAT"),
|
|
110
|
+
cell: ({ row: row2 }) => /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row2.original.tax })
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
accessorKey: "gross",
|
|
114
|
+
header: t("billing.statements.vat.gross", "Gross"),
|
|
115
|
+
cell: ({ row: row2 }) => /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row2.original.gross })
|
|
116
|
+
}
|
|
117
|
+
],
|
|
73
118
|
[t]
|
|
74
119
|
);
|
|
75
120
|
if (error) {
|
|
@@ -95,7 +140,7 @@ function BillingStatementDetailPage(props) {
|
|
|
95
140
|
/* @__PURE__ */ jsx(DetailField, { label: t("billing.statements.columns.opening", "Opening"), children: /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row.opening_balance }) }),
|
|
96
141
|
/* @__PURE__ */ jsx(DetailField, { label: t("billing.statements.columns.topups", "Top-ups"), children: /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row.total_topups }) }),
|
|
97
142
|
/* @__PURE__ */ jsx(DetailField, { label: t("billing.statements.columns.usage", "Usage"), children: /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row.total_usage }) }),
|
|
98
|
-
/* @__PURE__ */ jsx(DetailField, { label: t("billing.statements.columns.
|
|
143
|
+
/* @__PURE__ */ jsx(DetailField, { label: t("billing.statements.columns.charges", "Charges"), children: /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row.total_recurring }) }),
|
|
99
144
|
/* @__PURE__ */ jsx(DetailField, { label: t("billing.statements.columns.adjustments", "Adjustments"), children: /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums", children: row.total_adjustments }) }),
|
|
100
145
|
/* @__PURE__ */ jsx(DetailField, { label: t("billing.statements.columns.closing", "Closing"), children: /* @__PURE__ */ jsxs("span", { className: "font-mono tabular-nums", children: [
|
|
101
146
|
row.closing_balance,
|
|
@@ -117,9 +162,19 @@ function BillingStatementDetailPage(props) {
|
|
|
117
162
|
title: t("billing.statements.detail.breakdown", "Period-close charges"),
|
|
118
163
|
columns,
|
|
119
164
|
data: breakdown,
|
|
120
|
-
|
|
165
|
+
pagination: { page: 1, pageSize: 100, total: breakdown.length, totalPages: 1, onPageChange: () => void 0 },
|
|
166
|
+
emptyState: /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("billing.statements.detail.no_charges", "No charges at this bill date.") })
|
|
167
|
+
}
|
|
168
|
+
) }),
|
|
169
|
+
showVat ? /* @__PURE__ */ jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsx(
|
|
170
|
+
DataTable,
|
|
171
|
+
{
|
|
172
|
+
title: t("billing.statements.detail.vat_summary", "VAT summary"),
|
|
173
|
+
columns: vatColumns,
|
|
174
|
+
data: vatSummary,
|
|
175
|
+
pagination: { page: 1, pageSize: 100, total: vatSummary.length, totalPages: 1, onPageChange: () => void 0 }
|
|
121
176
|
}
|
|
122
|
-
) })
|
|
177
|
+
) }) : null
|
|
123
178
|
] })
|
|
124
179
|
] });
|
|
125
180
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/billing/backend/billing/statements/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useParams } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { DetailCard, DetailField } from '../../../../components/DetailFields'\nimport { statementStatusVariant } from '../page'\n\ntype BreakdownEntry = {\n billing_item_id: string\n billing_type: string\n uom_code?: string\n quantity: string\n amount: string\n}\n\ntype StatementRow = {\n id: string\n bill_account_id: string\n bill_run_id: string | null\n period_start: string\n period_end: string\n currency_code: string\n opening_balance: string\n total_topups: string\n total_usage: string\n total_recurring: string\n total_adjustments: string\n closing_balance: string\n debit_breakdown: { items?: BreakdownEntry[] } | null\n status: string\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const d = new Date(value)\n return Number.isNaN(d.getTime()) ? value : d.toLocaleDateString()\n}\n\nexport default function BillingStatementDetailPage(props: { params?: { id?: string } }) {\n const t = useT()\n const urlParams = useParams<{ id: string }>()\n const statementId = (props.params?.id ?? urlParams?.id ?? '') as string\n\n const [row, setRow] = React.useState<StatementRow | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n let cancelled = false\n if (!statementId) return\n setLoading(true)\n readApiResultOrThrow<{ items: StatementRow[] }>(\n `/api/billing/statements?id=${statementId}&pageSize=1`,\n )\n .then((res) => {\n if (cancelled) return\n const found = res.items?.[0]\n if (!found) setError(t('billing.statements.detail.not_found', 'Statement not found'))\n else setRow(found)\n })\n .catch(() => {\n if (!cancelled)\n setError(t('billing.statements.detail.load_error', 'Failed to load statement'))\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [statementId, t])\n\n const breakdown = row?.debit_breakdown?.items ?? []\n const columns = React.useMemo<ColumnDef<BreakdownEntry>[]>(\n () => [\n {\n accessorKey: 'billing_type',\n header: t('billing.statements.breakdown.type', 'Type'),\n cell: ({ row }) => <Tag variant=\"info\">{row.original.billing_type}</Tag>,\n },\n {\n accessorKey: 'billing_item_id',\n header: t('billing.statements.breakdown.item', 'Item'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.billing_item_id}</span>,\n },\n {\n accessorKey: 'quantity',\n header: t('billing.statements.breakdown.quantity', 'Qty'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.quantity}</span>,\n },\n {\n accessorKey: 'amount',\n header: t('billing.statements.breakdown.amount', 'Amount'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.amount}</span>,\n },\n ],\n [t],\n )\n\n if (error) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n if (loading || !row) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('billing.statements.detail.loading', 'Loading statement\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/billing/statements\"\n entityTypeLabel={t('billing.statements.detail.title', 'Statement')}\n title={`${formatDate(row.period_start)} \u2013 ${formatDate(row.period_end)}`}\n statusBadge={\n <Tag variant={statementStatusVariant(row.status)}>\n {t(`billing.statements.status.${row.status}`, row.status)}\n </Tag>\n }\n actionsContent={\n <Button asChild variant=\"outline\">\n <Link href={`/backend/billing/accounts/${row.bill_account_id}`}>\n {t('billing.statements.detail.view_account', 'View account')}\n </Link>\n </Button>\n }\n />\n <PageBody>\n <DetailCard>\n <DetailField label={t('billing.statements.columns.opening', 'Opening')}>\n <span className=\"font-mono tabular-nums\">{row.opening_balance}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.topups', 'Top-ups')}>\n <span className=\"font-mono tabular-nums\">{row.total_topups}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.usage', 'Usage')}>\n <span className=\"font-mono tabular-nums\">{row.total_usage}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.recurring', 'Recurring')}>\n <span className=\"font-mono tabular-nums\">{row.total_recurring}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.adjustments', 'Adjustments')}>\n <span className=\"font-mono tabular-nums\">{row.total_adjustments}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.closing', 'Closing')}>\n <span className=\"font-mono tabular-nums\">\n {row.closing_balance} {row.currency_code}\n </span>\n </DetailField>\n <DetailField label={t('billing.statements.detail.bill_run', 'Bill Run')}>\n {row.bill_run_id ? (\n <Link\n href={`/backend/billing/runs/${row.bill_run_id}`}\n className=\"font-mono text-xs hover:underline\"\n >\n {row.bill_run_id}\n </Link>\n ) : (\n '\u2014'\n )}\n </DetailField>\n </DetailCard>\n <div className=\"mt-6\">\n <DataTable\n title={t('billing.statements.detail.breakdown', 'Period-close charges')}\n columns={columns}\n data={breakdown}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('billing.statements.detail.no_charges', 'No recurring charges this period.')}\n </div>\n }\n />\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useParams } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { BillingDataTable as DataTable } from '../../../../components/BillingDataTable'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { DetailCard, DetailField } from '../../../../components/DetailFields'\nimport { statementStatusVariant } from '../page'\n\ntype BreakdownEntry = {\n billing_item_id: string\n billing_type: string\n uom_code?: string\n quantity: string\n /** What left the balance. */\n amount: string\n // Spec F \u2014 older statements do not carry these.\n description?: string\n net?: string\n tax?: string\n tax_rate?: string\n service_period_start?: string | null\n service_period_end?: string | null\n}\n\ntype VatSummaryEntry = { rate: string; net: string; tax: string; gross: string }\n\ntype StatementRow = {\n id: string\n bill_account_id: string\n bill_run_id: string | null\n period_start: string\n period_end: string\n currency_code: string\n opening_balance: string\n total_topups: string\n total_usage: string\n total_recurring: string\n total_adjustments: string\n closing_balance: string\n debit_breakdown: { items?: BreakdownEntry[] } | null\n vat_summary?: VatSummaryEntry[] | null\n status: string\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const d = new Date(value)\n return Number.isNaN(d.getTime()) ? value : d.toLocaleDateString()\n}\n\nexport default function BillingStatementDetailPage(props: { params?: { id?: string } }) {\n const t = useT()\n const urlParams = useParams<{ id: string }>()\n const statementId = (props.params?.id ?? urlParams?.id ?? '') as string\n\n const [row, setRow] = React.useState<StatementRow | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n let cancelled = false\n if (!statementId) return\n setLoading(true)\n readApiResultOrThrow<{ items: StatementRow[] }>(\n `/api/billing/statements?id=${statementId}&pageSize=1`,\n )\n .then((res) => {\n if (cancelled) return\n const found = res.items?.[0]\n if (!found) setError(t('billing.statements.detail.not_found', 'Statement not found'))\n else setRow(found)\n })\n .catch(() => {\n if (!cancelled)\n setError(t('billing.statements.detail.load_error', 'Failed to load statement'))\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [statementId, t])\n\n const breakdown = row?.debit_breakdown?.items ?? []\n const vatSummary = row?.vat_summary ?? []\n // Spec F: VAT columns only when VAT was charged at consumption.\n const showVat = vatSummary.length > 0\n const columns = React.useMemo<ColumnDef<BreakdownEntry>[]>(\n () => [\n {\n accessorKey: 'billing_type',\n header: t('billing.statements.breakdown.type', 'Type'),\n cell: ({ row }) => <Tag variant=\"info\">{row.original.billing_type}</Tag>,\n },\n {\n accessorKey: 'billing_item_id',\n header: t('billing.statements.breakdown.item', 'Item'),\n cell: ({ row }) => (\n <Link href={`/backend/billing/items/${row.original.billing_item_id}`} className=\"text-sm text-primary hover:underline\">\n {row.original.description ?? row.original.billing_item_id}\n </Link>\n ),\n },\n {\n accessorKey: 'service_period_start',\n header: t('billing.statements.breakdown.service_period', 'Service period'),\n cell: ({ row }) =>\n row.original.service_period_start\n ? `${row.original.service_period_start} \u2013 ${row.original.service_period_end ?? '\u2026'}`\n : '\u2014',\n },\n {\n accessorKey: 'quantity',\n header: t('billing.statements.breakdown.quantity', 'Qty'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.quantity}</span>,\n },\n ...(showVat\n ? [\n {\n accessorKey: 'net',\n header: t('billing.statements.breakdown.net', 'Net'),\n cell: ({ row }: { row: { original: BreakdownEntry } }) => (\n <span className=\"font-mono tabular-nums\">{row.original.net ?? '\u2014'}</span>\n ),\n } satisfies ColumnDef<BreakdownEntry>,\n {\n accessorKey: 'tax',\n header: t('billing.statements.breakdown.vat', 'VAT'),\n cell: ({ row }: { row: { original: BreakdownEntry } }) =>\n row.original.tax !== undefined ? (\n <span className=\"font-mono tabular-nums\">\n {row.original.tax} ({row.original.tax_rate ?? '0'} %)\n </span>\n ) : (\n '\u2014'\n ),\n } satisfies ColumnDef<BreakdownEntry>,\n ]\n : []),\n {\n accessorKey: 'amount',\n header: t('billing.statements.breakdown.charged', 'Charged to balance'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.amount}</span>,\n },\n ],\n [showVat, t],\n )\n const vatColumns = React.useMemo<ColumnDef<VatSummaryEntry>[]>(\n () => [\n { accessorKey: 'rate', header: t('billing.statements.vat.rate', 'VAT rate'), cell: ({ row }) => `${row.original.rate} %` },\n {\n accessorKey: 'net',\n header: t('billing.statements.breakdown.net', 'Net'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.net}</span>,\n },\n {\n accessorKey: 'tax',\n header: t('billing.statements.breakdown.vat', 'VAT'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.tax}</span>,\n },\n {\n accessorKey: 'gross',\n header: t('billing.statements.vat.gross', 'Gross'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.gross}</span>,\n },\n ],\n [t],\n )\n\n if (error) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n if (loading || !row) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('billing.statements.detail.loading', 'Loading statement\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/billing/statements\"\n entityTypeLabel={t('billing.statements.detail.title', 'Statement')}\n title={`${formatDate(row.period_start)} \u2013 ${formatDate(row.period_end)}`}\n statusBadge={\n <Tag variant={statementStatusVariant(row.status)}>\n {t(`billing.statements.status.${row.status}`, row.status)}\n </Tag>\n }\n actionsContent={\n <Button asChild variant=\"outline\">\n <Link href={`/backend/billing/accounts/${row.bill_account_id}`}>\n {t('billing.statements.detail.view_account', 'View account')}\n </Link>\n </Button>\n }\n />\n <PageBody>\n <DetailCard>\n <DetailField label={t('billing.statements.columns.opening', 'Opening')}>\n <span className=\"font-mono tabular-nums\">{row.opening_balance}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.topups', 'Top-ups')}>\n <span className=\"font-mono tabular-nums\">{row.total_topups}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.usage', 'Usage')}>\n <span className=\"font-mono tabular-nums\">{row.total_usage}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.charges', 'Charges')}>\n <span className=\"font-mono tabular-nums\">{row.total_recurring}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.adjustments', 'Adjustments')}>\n <span className=\"font-mono tabular-nums\">{row.total_adjustments}</span>\n </DetailField>\n <DetailField label={t('billing.statements.columns.closing', 'Closing')}>\n <span className=\"font-mono tabular-nums\">\n {row.closing_balance} {row.currency_code}\n </span>\n </DetailField>\n <DetailField label={t('billing.statements.detail.bill_run', 'Bill Run')}>\n {row.bill_run_id ? (\n <Link\n href={`/backend/billing/runs/${row.bill_run_id}`}\n className=\"font-mono text-xs hover:underline\"\n >\n {row.bill_run_id}\n </Link>\n ) : (\n '\u2014'\n )}\n </DetailField>\n </DetailCard>\n <div className=\"mt-6\">\n <DataTable\n title={t('billing.statements.detail.breakdown', 'Period-close charges')}\n columns={columns}\n data={breakdown}\n pagination={{ page: 1, pageSize: 100, total: breakdown.length, totalPages: 1, onPageChange: () => undefined }}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('billing.statements.detail.no_charges', 'No charges at this bill date.')}\n </div>\n }\n />\n </div>\n {showVat ? (\n <div className=\"mt-6\">\n <DataTable\n title={t('billing.statements.detail.vat_summary', 'VAT summary')}\n columns={vatColumns}\n data={vatSummary}\n pagination={{ page: 1, pageSize: 100, total: vatSummary.length, totalPages: 1, onPageChange: () => undefined }}\n />\n </div>\n ) : null}\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["row"]
|
|
7
7
|
}
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
5
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
6
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
7
|
-
import { DataTable } from "
|
|
7
|
+
import { BillingDataTable as DataTable } from "../../../components/BillingDataTable.js";
|
|
8
8
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
9
9
|
import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
10
10
|
import { fetchAccountNames } from "../../../components/accountNames.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/billing/backend/billing/statements/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '
|
|
5
|
-
"mappings": ";AAoHY,cA8BF,YA9BE;AAnHZ,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAE1B,SAAS,YAAY;AAErB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,iBAAiB;
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { BillingDataTable as DataTable } from '../../../components/BillingDataTable'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { fetchAccountNames } from '../../../components/accountNames'\n\n/**\n * Consumption statements admin list (SPEC-002 P3). Non-fiscal period summaries\n * for prepaid accounts \u2014 opening / top-ups / usage / recurring / closing.\n */\n\ntype StatementRow = {\n id: string\n bill_account_id: string\n period_start: string\n period_end: string\n currency_code: string\n opening_balance: string\n total_topups: string\n total_usage: string\n total_recurring: string\n closing_balance: string\n status: string\n}\n\ntype ListResponse = { items: StatementRow[]; total: number; totalPages: number }\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const d = new Date(value)\n return Number.isNaN(d.getTime()) ? value : d.toLocaleDateString()\n}\n\nexport function statementStatusVariant(status: string): 'success' | 'info' {\n return status === 'sent' ? 'success' : 'info'\n}\n\nexport default function BillingStatementsListPage() {\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<StatementRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(25)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [accountNames, setAccountNames] = React.useState<Record<string, string>>({})\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'status',\n label: t('billing.statements.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'generated', label: t('billing.statements.status.generated', 'Generated') },\n { value: 'sent', label: t('billing.statements.status.sent', 'Sent') },\n ],\n },\n ],\n [t],\n )\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(pageSize),\n sortField: 'periodEnd',\n sortDir: 'desc',\n })\n if (typeof filters.status === 'string' && filters.status) params.set('status', filters.status)\n const result = await readApiResultOrThrow<ListResponse>(\n `/api/billing/statements?${params.toString()}`,\n )\n const items = result.items ?? []\n setRows(items)\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n try {\n const names = await fetchAccountNames(items.map((r) => r.bill_account_id))\n setAccountNames((prev) => ({ ...prev, ...names }))\n } catch {\n // Name resolution is best-effort \u2014 fall back to the id in the cell.\n }\n } finally {\n setLoading(false)\n }\n }, [filters.status, page, pageSize])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const columns = React.useMemo<ColumnDef<StatementRow>[]>(\n () => [\n {\n accessorKey: 'period_end',\n header: t('billing.statements.columns.period', 'Period'),\n cell: ({ row }) =>\n `${formatDate(row.original.period_start)} \u2013 ${formatDate(row.original.period_end)}`,\n },\n {\n accessorKey: 'bill_account_id',\n header: t('billing.statements.columns.account', 'Account'),\n cell: ({ row }) => {\n const name = accountNames[row.original.bill_account_id]\n return name ? (\n <span className=\"text-sm\">{name}</span>\n ) : (\n <span className=\"font-mono text-xs\">{row.original.bill_account_id}</span>\n )\n },\n },\n {\n accessorKey: 'opening_balance',\n header: t('billing.statements.columns.opening', 'Opening'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.opening_balance}</span>,\n },\n {\n accessorKey: 'total_topups',\n header: t('billing.statements.columns.topups', 'Top-ups'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.total_topups}</span>,\n },\n {\n accessorKey: 'total_usage',\n header: t('billing.statements.columns.usage', 'Usage'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.total_usage}</span>,\n },\n {\n accessorKey: 'total_recurring',\n header: t('billing.statements.columns.recurring', 'Recurring'),\n cell: ({ row }) => <span className=\"font-mono tabular-nums\">{row.original.total_recurring}</span>,\n },\n {\n accessorKey: 'closing_balance',\n header: t('billing.statements.columns.closing', 'Closing'),\n cell: ({ row }) => (\n <span className=\"font-mono tabular-nums\">\n {row.original.closing_balance} {row.original.currency_code}\n </span>\n ),\n },\n {\n accessorKey: 'status',\n header: t('billing.statements.columns.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={statementStatusVariant(row.original.status)}>\n {t(`billing.statements.status.${row.original.status}`, row.original.status)}\n </Tag>\n ),\n },\n ],\n [t, accountNames],\n )\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('billing.statements.title', 'Statements')}\n refreshButton={{\n label: t('billing.common.refresh', 'Refresh'),\n onRefresh: loadRows,\n isRefreshing: loading,\n }}\n columnChooser={{ auto: true }}\n perspective={{ tableId: 'billing-statements' }}\n columns={columns}\n data={rows}\n onRowClick={(row) => router.push(`/backend/billing/statements/${row.id}`)}\n isLoading={loading}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={setFilters}\n onFiltersClear={() => setFilters({})}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
5
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
6
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
7
|
-
import { DataTable } from "
|
|
7
|
+
import { BillingDataTable as DataTable } from "../../../components/BillingDataTable.js";
|
|
8
8
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
9
9
|
import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
10
10
|
import { fetchAccountNames } from "../../../components/accountNames.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/billing/backend/billing/topups/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '
|
|
5
|
-
"mappings": ";AAuHU,SAQiB,KARjB;AAtHV,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAE1B,SAAS,YAAY;AAErB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,iBAAiB;
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { BillingDataTable as DataTable } from '../../../components/BillingDataTable'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { fetchAccountNames } from '../../../components/accountNames'\n\n/**\n * Registered top-ups admin list (SPEC-002 P2). Read-only \u2014 top-ups are\n * initiated from a prepaid account's detail page and progress through the\n * gateway capture lifecycle.\n */\n\ntype TopupRow = {\n id: string\n bill_account_id: string\n status: string\n amount: string\n currency_code: string\n provider_key: string\n receipt_invoice_id: string | null\n created_at: string\n}\n\ntype ListResponse = { items: TopupRow[]; total: number; totalPages: number }\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const d = new Date(value)\n return Number.isNaN(d.getTime()) ? value : d.toLocaleString()\n}\n\nexport function statusVariant(status: string): 'success' | 'info' | 'error' | 'default' {\n if (status === 'captured') return 'success'\n if (status === 'pending') return 'info'\n if (status === 'failed') return 'error'\n return 'default' // cancelled / expired\n}\n\nexport default function BillingTopupsListPage() {\n const t = useT()\n const router = useRouter()\n const [rows, setRows] = React.useState<TopupRow[]>([])\n const [page, setPage] = React.useState(1)\n const [pageSize] = React.useState(25)\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [search, setSearch] = React.useState('')\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const [accountNames, setAccountNames] = React.useState<Record<string, string>>({})\n\n const filterDefs = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'status',\n label: t('billing.topups.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'pending', label: t('billing.topups.status.pending', 'Pending') },\n { value: 'captured', label: t('billing.topups.status.captured', 'Captured') },\n { value: 'failed', label: t('billing.topups.status.failed', 'Failed') },\n { value: 'cancelled', label: t('billing.topups.status.cancelled', 'Cancelled') },\n { value: 'expired', label: t('billing.topups.status.expired', 'Expired') },\n ],\n },\n ],\n [t],\n )\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(pageSize),\n sortField: 'createdAt',\n sortDir: 'desc',\n })\n if (search.trim()) params.set('search', search.trim())\n if (typeof filters.status === 'string' && filters.status) params.set('status', filters.status)\n const result = await readApiResultOrThrow<ListResponse>(\n `/api/billing/topups?${params.toString()}`,\n )\n const items = result.items ?? []\n setRows(items)\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n try {\n const names = await fetchAccountNames(items.map((r) => r.bill_account_id))\n setAccountNames((prev) => ({ ...prev, ...names }))\n } catch {\n // Name resolution is best-effort \u2014 fall back to the id in the cell.\n }\n } finally {\n setLoading(false)\n }\n }, [filters.status, page, pageSize, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const columns = React.useMemo<ColumnDef<TopupRow>[]>(\n () => [\n {\n accessorKey: 'created_at',\n header: t('billing.topups.columns.created', 'Created'),\n cell: ({ row }) => formatDate(row.original.created_at),\n },\n {\n accessorKey: 'amount',\n header: t('billing.topups.columns.amount', 'Amount'),\n cell: ({ row }) => (\n <span className=\"font-mono tabular-nums\">\n {row.original.amount} {row.original.currency_code}\n </span>\n ),\n },\n {\n accessorKey: 'provider_key',\n header: t('billing.topups.columns.provider', 'Provider'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.provider_key}</span>,\n },\n {\n accessorKey: 'status',\n header: t('billing.topups.columns.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={statusVariant(row.original.status)}>\n {t(`billing.topups.status.${row.original.status}`, row.original.status)}\n </Tag>\n ),\n },\n {\n accessorKey: 'bill_account_id',\n header: t('billing.topups.columns.account', 'Account'),\n cell: ({ row }) => {\n const name = accountNames[row.original.bill_account_id]\n return name ? (\n <span className=\"text-sm\">{name}</span>\n ) : (\n <span className=\"font-mono text-xs\">{row.original.bill_account_id}</span>\n )\n },\n },\n ],\n [t, accountNames],\n )\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('billing.topups.title', 'Top-ups')}\n refreshButton={{\n label: t('billing.common.refresh', 'Refresh'),\n onRefresh: loadRows,\n isRefreshing: loading,\n }}\n columnChooser={{ auto: true }}\n perspective={{ tableId: 'billing-topups' }}\n columns={columns}\n data={rows}\n onRowClick={(row) => router.push(`/backend/billing/topups/${row.id}`)}\n isLoading={loading}\n pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={setFilters}\n onFiltersClear={() => setFilters({})}\n searchValue={search}\n onSearchChange={setSearch}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|