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
This commit is contained in:
Mattsson
2026-07-15 15:53:15 +02:00
committed by GitHub
parent a558c75678
commit 072aedeaf9
116 changed files with 5708 additions and 669 deletions
+18
View File
@@ -905,6 +905,9 @@ async function commitMarkInvoicePaid(
.single()
if (invoiceError || !invoice) return { error: 'Invoice not found', status: 404 }
if (invoice.credited_invoice_id) {
return { error: 'Kreditfakturor kan inte markeras som betalda.', status: 409 }
}
if (invoice.status !== 'sent' && invoice.status !== 'overdue') {
return { error: 'Invoice can only be marked as paid when status is "sent" or "overdue"', status: 409 }
}
@@ -1143,6 +1146,12 @@ async function commitSendInvoice(
.single()
if (invoiceError || !invoice) return { error: 'Invoice not found', status: 404 }
if (invoice.credited_invoice_id) {
return {
error: 'Credit notes must be issued through the invoice send flow',
status: 409,
}
}
// partially_paid/credited imply the invoice was already issued too: the
// status flip below would regress them to 'sent' (PR #666 review, ASVS V2.3).
if (['sent', 'paid', 'overdue', 'partially_paid', 'credited'].includes(invoice.status)) {
@@ -1312,6 +1321,12 @@ async function commitMarkInvoiceSent(
.single()
if (invoiceError || !invoice) return { error: 'Invoice not found', status: 404 }
if (invoice.credited_invoice_id) {
return {
error: 'Credit notes must be issued through the invoice send flow',
status: 409,
}
}
if (invoice.status !== 'draft') return { error: 'Only draft invoices can be marked as sent', status: 409 }
try {
@@ -1374,6 +1389,9 @@ async function commitMatchTransactionInvoice(
.single()
if (invError || !invoice) return { error: 'Invoice not found', status: 404 }
if (invoice.credited_invoice_id) {
return { error: 'Kreditfakturor kan inte registreras som betalda.', status: 409 }
}
if (!['sent', 'overdue', 'partially_paid'].includes(invoice.status)) {
return { error: 'Invoice is not in a matchable state', status: 409 }
}