feat(payments): betalfil UI (betalfil 3/3) (#1505)

* feat(payments): betalfil UI (betalfil 3/3)

Bulk-select + Skapa betalfil bulkbar on the supplier-invoices list,
preview dialog with per-line editable amount/date and exclusion reasons,
payment-files history page with re-download, cancel and a sequential
bulk mark-paid (duplicate guard respected, never forced), I betalfil
chip on rows in active batches, clearing/kontonummer fields on the
supplier form, and the supplier_payment_files namespace in sv+en.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sandbox): make the demo AP data betalfil-ready

Demo supplier bankgiro numbers were not Luhn-valid, the unpaid demo
invoice had remaining_amount 0 (no trigger derives it, so the list said
0 kr kvar att betala), and the company had no IBAN/BIC, all of which
excluded the seeded data from the betalfil flow. Numbers swapped for
Luhn-valid ones (991-2346 is Bankgirot's test number), a valid OCR added,
and both bulk-insert rows set remaining_amount explicitly per the
PostgREST normalization rule already documented inline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-10 20:07:04 +02:00
committed by GitHub
co-authored by Claude Fable 5 Jakob Wennberg
parent 576a34750a
commit f0bedc14af
10 changed files with 1134 additions and 9 deletions
+19 -2
View File
@@ -167,6 +167,12 @@ export async function POST(request: Request) {
next_invoice_number: 5,
next_delivery_note_number: 1,
invoice_default_days: 30,
// Sender bank details: the pain.001 debtor for the betalfil demo.
// Example IBAN from the Swedish IBAN documentation range; BIC derives
// from it being an SEB-style example. Demo-only values.
iban: 'SE3550000000054910000003',
bic: 'ESSESESS',
bankgiro: '991-2346',
onboarding_step: 6,
onboarding_complete: true,
initial_setup_path: 'fresh',
@@ -867,7 +873,9 @@ export async function POST(request: Request) {
org_number: '5559000001',
vat_number: 'SE555900000101',
email: 'demo+telekom@example.com',
bankgiro: '5559-0001',
// Luhn-valid (Bankgirot check digit): the betalfil flow validates
// payee numbers, so demo suppliers must carry numbers that pass.
bankgiro: '5559-0004',
address_line1: 'Demovägen 10',
postal_code: '111 22',
city: 'Stockholm',
@@ -881,7 +889,7 @@ export async function POST(request: Request) {
supplier_type: 'swedish_business',
org_number: '5559000002',
vat_number: 'SE555900000201',
bankgiro: '5559-0002',
bankgiro: '5559-0012',
address_line1: 'Demovägen 11',
postal_code: '111 22',
city: 'Stockholm',
@@ -923,6 +931,9 @@ export async function POST(request: Request) {
payment_reference: '47112026031',
paid_at: toDateStr(fifteenDaysAgo),
paid_amount: 600,
// Same normalization rule as paid_amount below: the other row in
// this bulk insert sets remaining_amount, so this one must too.
remaining_amount: 0,
},
{
user_id: userId,
@@ -938,11 +949,17 @@ export async function POST(request: Request) {
subtotal: 240,
vat_amount: 28.80,
total: 268.80,
// Luhn-valid OCR so the betalfil preview demos the structured
// reference path instead of the invoice-number fallback.
payment_reference: '882456',
// Must be set explicitly: PostgREST normalizes columns across
// rows in a bulk insert, so omitting paid_amount here while the
// first row sets it sends null instead of falling through to the
// schema default (0), violating the NOT NULL constraint.
paid_amount: 0,
// No trigger derives this; without it the unpaid demo invoice
// shows "0 kr kvar att betala" and cannot join a betalfil.
remaining_amount: 268.80,
},
])
.select('id, supplier_invoice_number')
+2
View File
@@ -94,6 +94,8 @@ export const PUT = withRouteContext(
bank_account: body.bank_account,
iban: body.iban,
bic: body.bic,
clearing_number: body.clearing_number,
account_number: body.account_number,
default_expense_account: body.default_expense_account,
default_payment_terms: body.default_payment_terms,
default_currency: body.default_currency,
+2
View File
@@ -63,6 +63,8 @@ export const POST = withRouteContext(
bank_account: body.bank_account,
iban: body.iban,
bic: body.bic,
clearing_number: body.clearing_number,
account_number: body.account_number,
default_expense_account: body.default_expense_account,
default_payment_terms: body.default_payment_terms || 30,
default_currency: body.default_currency || 'SEK',