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
@@ -571,6 +571,47 @@ describe('POST :id/match-invoice', () => {
expect((await res.json()).error.code).toBe('MATCH_INVOICE_TX_ALREADY_LINKED')
})
it('rejects a credit note before creating a payment journal entry', async () => {
mockServiceClient.mockReturnValue(
makeFlexibleSupabase({
company_members: { data: { company_id: COMPANY_ID, role: 'owner' }, error: null },
transactions: {
data: {
id: TX_ID,
amount: 12500,
date: '2026-05-12',
currency: 'SEK',
invoice_id: null,
},
error: null,
},
invoices: {
data: {
id: INV_ID,
status: 'sent',
document_type: 'invoice',
total: -12500,
credited_invoice_id: 'ffffffff-ffff-4fff-8fff-ffffffffffff',
},
error: null,
},
}),
)
const res = await matchInvoicePOST(
makeRequest(
`https://x.test/api/v1/companies/${COMPANY_ID}/transactions/${TX_ID}/match-invoice`,
{ invoice_id: INV_ID },
),
txParams(TX_ID),
)
expect(res.status).toBe(400)
expect((await res.json()).error.code).toBe('MATCH_INVOICE_CREDIT_NOTE')
expect(createInvPmtJE).not.toHaveBeenCalled()
expect(createInvCashJE).not.toHaveBeenCalled()
})
// The v1 route threads resolveSettlementAccount(transaction.cash_account_id)
// exactly like the dashboard route and the agent/MCP commit path; these
// regression tests were missing here (flagged in triage on #987) even
@@ -180,6 +180,11 @@ export const POST = withApiV1<{ params: Promise<{ companyId: string; id: string
details: { documentType: docType },
})
}
if (invoice.credited_invoice_id) {
return v1ErrorResponseFromCode('MATCH_INVOICE_CREDIT_NOTE', txLog, {
requestId: ctx.requestId,
})
}
if (
invoice.status !== 'sent' &&
invoice.status !== 'overdue' &&