fix(bookkeeping): let a rättelseverifikation be stornoed; unblock aged supplier-invoice deletion (#1204)
* fix(bookkeeping): let a rättelseverifikation be stornoed; unblock aged supplier-invoice deletion A user who corrected a booking (storno + rättelse) and then discovered the affärshändelse was already booked by another verifikat had no sanctioned way out: reverseEntry refused source_type 'correction' alongside 'storno', and correctEntry rightly rejects a zeroing rättelse (BFL 5 kap 5 §). The same guard also broke uncategorize-after-rättelse, since bank transactions are relinked to the correction entry. - reverseEntry now blocks only 'storno' (storno-of-a-storno keeps the chain ambiguity problem); a correction entry is a regular live verifikat and can be stornoed, with correction_of_id keeping the chain traceable. - CANNOT_REVERSE_STORNO copy narrowed to stornos + remediation hint. - Supplier-invoice DELETE now allows unbooked, unpaid invoices in registered/approved/overdue: the daily overdue cron flipped unbooked invoices past due_date into a state where deletion was blocked forever. Orphan-safety checks (registration JE, payments, accrual schedule) are what actually protect the books. UI shows the delete button accordingly. - LinkVoucherPicker showed customer-side copy (kundfordran/1510) in supplier-invoice mode; supplier mode now explains the 2440-debit requirement, including why a direct-cost verifikat cannot be linked. Support case 2026-07-26 (marcus@). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(supplier-invoices): review fixes: fail-closed orphan lookups, hide delete when payments loaded - The payment and accrual-schedule lookups in DELETE now fail closed: a lookup error returns 500 instead of reading as "nothing linked" and letting the delete proceed unverified. - The delete button also requires the loaded payment list to be empty, matching the server predicate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: authorize 'approved' in supplier-invoice delete allow-list (compliance-swarm V2.3) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
968161b42b
commit
1270b6daeb
@@ -733,11 +733,16 @@ export async function reverseEntry(
|
||||
throw new CannotReverseNonPostedError(original.status)
|
||||
}
|
||||
|
||||
// A storno or correction entry must never itself be reversed: a
|
||||
// storno-of-a-storno makes the original verifikat's cancellation chain
|
||||
// ambiguous (BFL 5 kap 5§). The UI hides "Återför" for these source types;
|
||||
// this is the server-side backstop against a direct API call.
|
||||
if (original.source_type === 'storno' || original.source_type === 'correction') {
|
||||
// A storno entry must never itself be reversed: a storno-of-a-storno makes
|
||||
// the original verifikat's cancellation chain ambiguous (BFL 5 kap 5§). A
|
||||
// correction entry, by contrast, is a regular live verifikation and must
|
||||
// stay reversible: it can be a duplicate (the affärshändelse already booked
|
||||
// by another verifikat) or plain wrong, and blocking it left users with no
|
||||
// sanctioned way out (support case 2026-07-26). Its correction_of_id link
|
||||
// keeps the chain traceable either way; the original it corrected stays
|
||||
// 'reversed'. The UI hides "Återför" for stornos; this is the server-side
|
||||
// backstop against a direct API call.
|
||||
if (original.source_type === 'storno') {
|
||||
throw new CannotReverseStornoError(original.source_type)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user