fix(supplier-invoices): freeze verifikat-critical fields once the registration entry is posted (#1249)
* fix(supplier-invoices): freeze verifikat-critical fields once the registration entry is posted invoice_date becomes the registration verifikat's entry_date and supplier_invoice_number goes into its description, but both stayed freely writable through the shared UpdateSupplierInvoiceSchema. Editing either on a booked invoice moved the invoice row while the posted entry kept its original values: the two disagreed silently, nothing landed in journal_entry_rattelse_log, and the change bypassed both sanctioned rättelse paths (BFL 5 kap 5-7 §). Adds findLockedVerifikatFields() next to the other supplier-invoice lifecycle predicates and calls it from both writers (dashboard PUT and v1 PATCH, which also covers the API-key/MCP path). Only a differing value is refused, so a full-form resend of the stored value still succeeds, and due_date, payment_reference and notes stay editable for the aged-invoice flow (#1206). Fixes #1230 * fix(supplier-invoices): make the verifikat-field lock atomic with the write Review follow-up on #1230: the lock check read the row a moment before the update ran, so a registration entry posted in between let exactly the drift the guard exists to prevent slip through. When an update moves a verifikat-critical field on a row that read as unbooked, the write is now pinned with `registration_journal_entry_id is null`. A concurrent posting therefore matches zero rows: the dashboard route returns its existing SI_EDIT_CONFLICT ("reload and try again", and the retry hits the lock with the right message), and the v1 route re-reads to answer with SI_EDIT_VERIFIKAT_LOCKED plus reason=race rather than a guess. The pin is conditional on the update actually moving one of those fields, so metadata-only edits and full-form resends of unchanged values on a booked invoice keep working (#1206).
This commit is contained in:
@@ -1168,6 +1168,22 @@ const SUPPLIER_INVOICE: Record<string, StructuredErrorEntry> = {
|
||||
message_en:
|
||||
'Only unsettled supplier invoices can be edited. Paid, credited and reversed invoices are corrected with a credit note or a storno.',
|
||||
},
|
||||
SI_EDIT_VERIFIKAT_LOCKED: {
|
||||
httpStatus: 400,
|
||||
message_sv:
|
||||
'Fakturadatum och fakturanummer står på det bokförda verifikatet och kan inte ändras här. ' +
|
||||
'Rätta verifikatet (rättelse i öppen period, annars storno + ny bokföring) eller kreditera fakturan. ' +
|
||||
'Förfallodatum, betalningsreferens och anteckningar går fortfarande att ändra.',
|
||||
message_en:
|
||||
'Invoice date and invoice number are part of the posted verifikat and cannot be changed here. ' +
|
||||
'Correct the entry instead (inline rättelse in an open period, otherwise storno + re-book), or credit the invoice. ' +
|
||||
'due_date, payment_reference and notes remain editable.',
|
||||
remediation: {
|
||||
description:
|
||||
'Correct the registration verifikat through a sanctioned rättelse path, or credit the supplier invoice and register a corrected one.',
|
||||
tool: 'gnubok_correct_entry',
|
||||
},
|
||||
},
|
||||
SI_APPROVE_UPDATE_FAILED: {
|
||||
httpStatus: 500,
|
||||
message_sv: 'Kunde inte godkänna leverantörsfakturan.',
|
||||
|
||||
Reference in New Issue
Block a user