Files
accounted/lib/invoices/is-editable-draft.ts
T
Mattsson 072aedeaf9 Fix/supp ag fb (#1023)
* fix: prevent credit notes from entering payment flow

* fix: persist and display customer personal numbers

* feat: configure automatic invoice reminder days

* fix: issue credit notes through send flow

* chore: add repository agent guidance

* feat(mcp): route tools across user companies

* fix(articles): delete unused register entries

* feat(invoices): improve issued invoice actions

* feat(supplier-invoices): retain uploaded source documents

* docs: record implementation decisions

* feat: enhance customer personal number handling and validation

- Updated CustomerForm to allow personal numbers in the format of "********-1234" for individual customers.
- Added validation to ensure personal numbers are only accepted for individual customers in CreateCustomerSchema.
- Implemented masking and encryption for personal numbers to enhance data protection.
- Introduced new utility functions for masking and encrypting personal numbers.
- Added database migration to enforce unique constraints on credit note relationships and prevent duplicate entries.
- Enhanced error handling and logging for credit note issuance and invoice processing.
- Updated tests to cover new credit note creation guards and personal number handling.

* test: enhance list companies test with supabase query mocks
2026-07-15 15:53:15 +02:00

29 lines
1.2 KiB
TypeScript

/**
* A draft invoice (or proforma / delivery note) is editable in place: header
* fields AND line items: only while it has no committed verifikat. A journal
* entry is created when the invoice is sent (mark-sent / send) or, for
* kontantmetoden, at payment; once one exists, BFL immutability applies and the
* invoice must be corrected with a credit note instead. A self-billed invoice we
* received is the counterparty's document: never editable here. Credit-note
* drafts mirror an issued invoice and must not be changed into a different
* correction after creation.
*
* This is the single source of truth for that predicate. The PATCH route
* (app/api/invoices/[id]/route.ts) enforces it server-side; the detail and edit
* pages call it only to avoid opening a dead form: they are UX hints, not the
* trust boundary. Keeping all three on one function stops the rule from drifting.
*/
export function isEditableInvoiceDraft(invoice: {
status: string
journal_entry_id?: string | null
is_self_billed?: boolean | null
credited_invoice_id?: string | null
}): boolean {
return (
invoice.status === 'draft' &&
!invoice.journal_entry_id &&
!invoice.is_self_billed &&
!invoice.credited_invoice_id
)
}