feat(payments): supplier payment batch schema + pain.001 domain lib (betalfil 1/3) (#1500)
* feat(payments): supplier payment batch schema + pain.001 domain lib Betalfil for leverantorsfakturor, part 1 of 3. New tables supplier_payment_batches + supplier_payment_batch_items (RLS, immutable item snapshots, FK RESTRICT on invoices), payee/reference resolution, eligibility rules shared by preview and create, and a supplier-dialect pain.001.001.03 generator (SESBA 9900 BGNR / 9960 BBAN / clearing BBAN, SCOR for Luhn-valid OCR, Ustrd fallback, no SvcLvl/CtgyPurp). Deterministic regeneration: msg_id derives from the batch id, CreDtTm from created_at, so re-downloads are byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(payments): use lib/money helpers instead of raw ore rounding The naive-ore-round ratchet flags new Math.round(x*100)/100 sites; roundOre/sumOre/ORE_TOLERANCE are the sanctioned forms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(payments): classify batch tables in full-archive contract + fixture The no-phantom-columns contract requires every company-scoped table to be triaged in full-archive-export; the batch rows are underlag for the payments they initiated, so they dump with the archive. makeSupplier gains the clearing/account columns the Supplier type now carries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(payments): harden batch integrity per review Composite (id, company_id) FKs so items can never cross-link a batch and an invoice from different companies; BEFORE UPDATE trigger keeps batches immutable outside lifecycle + download metadata and one-way on created -> cancelled; active-batch lookup now fails closed (an error no longer reads as no active batches, which would have silently disabled the duplicate-batch guard); today derives from Europe/Stockholm, not UTC; pain.001 control sums add the amounts as rendered so CtrlSum always equals sum(InstdAmt); event-bus reset in test hooks; Danske LB date claim in DECISIONS verified against the primary page (the bot's 12 May date is the alias-initiation date, not LB retirement). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(payments): bind cancellation metadata to the cancel transition cancelled_at/cancelled_by may only be written by created -> cancelled; cancelled_by may still become NULL so the FK's ON DELETE SET NULL keeps working when the cancelling user's account is deleted (proven in pg). 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:
co-authored by
Claude Fable 5
Jakob Wennberg
parent
f0ce322a7c
commit
f776e375c5
@@ -763,6 +763,8 @@ export interface Supplier {
|
||||
bank_account: string | null
|
||||
iban: string | null
|
||||
bic: string | null
|
||||
clearing_number: string | null
|
||||
account_number: string | null
|
||||
|
||||
default_expense_account: string | null
|
||||
default_payment_terms: number
|
||||
@@ -774,6 +776,60 @@ export interface Supplier {
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
// Supplier payment batch (betalfil): an immutable snapshot of payment
|
||||
// instructions handed to the bank as a file. Generating or downloading a
|
||||
// batch books nothing; settlement stays in mark-paid / bank matching.
|
||||
export type SupplierPaymentBatchFormat = 'pain001' | 'bg_lb'
|
||||
export type SupplierPaymentBatchStatus = 'created' | 'cancelled'
|
||||
|
||||
export interface SupplierPaymentBatchDebtor {
|
||||
name: string
|
||||
org_number: string
|
||||
iban: string
|
||||
bic: string
|
||||
}
|
||||
|
||||
export interface SupplierPaymentBatch {
|
||||
id: string
|
||||
company_id: string
|
||||
user_id: string
|
||||
format: SupplierPaymentBatchFormat
|
||||
status: SupplierPaymentBatchStatus
|
||||
currency: string
|
||||
total_amount: number
|
||||
item_count: number
|
||||
/** pain.001 MsgId, fixed at creation; re-downloads reuse it verbatim. */
|
||||
msg_id: string
|
||||
debtor_snapshot: SupplierPaymentBatchDebtor
|
||||
file_generated_at: string | null
|
||||
download_count: number
|
||||
cancelled_at: string | null
|
||||
cancelled_by: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export type SupplierPaymentBatchPayeeType = 'bankgiro' | 'plusgiro' | 'bank_account'
|
||||
export type SupplierPaymentBatchReferenceType = 'ocr' | 'invoice_number'
|
||||
|
||||
export interface SupplierPaymentBatchItem {
|
||||
id: string
|
||||
batch_id: string
|
||||
company_id: string
|
||||
supplier_invoice_id: string
|
||||
amount: number
|
||||
payment_date: string
|
||||
payee_type: SupplierPaymentBatchPayeeType
|
||||
payee_bankgiro: string | null
|
||||
payee_plusgiro: string | null
|
||||
payee_clearing: string | null
|
||||
payee_account: string | null
|
||||
payee_name: string
|
||||
reference_type: SupplierPaymentBatchReferenceType
|
||||
reference: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
// Article (artikelregister): reusable invoice-line preset. NON-INVENTORY:
|
||||
// no stock fields and no inventory postings, by deliberate design.
|
||||
export type ArticleType = 'vara' | 'tjanst'
|
||||
@@ -1413,6 +1469,8 @@ export interface CreateSupplierInput {
|
||||
bank_account?: string
|
||||
iban?: string
|
||||
bic?: string
|
||||
clearing_number?: string
|
||||
account_number?: string
|
||||
default_expense_account?: string
|
||||
default_payment_terms?: number
|
||||
default_currency?: string
|
||||
|
||||
Reference in New Issue
Block a user