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:
@@ -587,6 +587,9 @@ export function makeCompanySettings(
|
||||
invoice_footer_text: null,
|
||||
invoice_email_texts: null,
|
||||
send_invoice_reminders: true,
|
||||
reminder_days_level_1: 15,
|
||||
reminder_days_level_2: 30,
|
||||
reminder_days_level_3: 45,
|
||||
reminder_fee_enabled: true,
|
||||
reminder_fee_amount: 60,
|
||||
reminder_interest_rate_override: null,
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getPool } from './setup'
|
||||
import { seedCompany } from './fixtures'
|
||||
|
||||
async function insertInvoice(input: {
|
||||
userId: string
|
||||
companyId: string
|
||||
number: string
|
||||
creditedInvoiceId?: string
|
||||
creationComplete?: boolean
|
||||
}): Promise<string> {
|
||||
const id = randomUUID()
|
||||
await getPool().query(
|
||||
`INSERT INTO public.invoices
|
||||
(id, user_id, company_id, invoice_number, invoice_date, due_date,
|
||||
credited_invoice_id, creation_complete)
|
||||
VALUES ($1, $2, $3, $4, '2026-07-15', '2026-07-15', $5, $6)`,
|
||||
[
|
||||
id,
|
||||
input.userId,
|
||||
input.companyId,
|
||||
input.number,
|
||||
input.creditedInvoiceId ?? null,
|
||||
input.creationComplete ?? true,
|
||||
],
|
||||
)
|
||||
return id
|
||||
}
|
||||
|
||||
describe('credit note creation and posting guards', () => {
|
||||
it('allows only one credit-note relationship per original and company', async () => {
|
||||
const { userId, companyId } = await seedCompany()
|
||||
const originalId = await insertInvoice({ userId, companyId, number: `F-${randomUUID()}` })
|
||||
|
||||
await insertInvoice({
|
||||
userId,
|
||||
companyId,
|
||||
number: `KR-${randomUUID()}`,
|
||||
creditedInvoiceId: originalId,
|
||||
})
|
||||
|
||||
await expect(
|
||||
insertInvoice({
|
||||
userId,
|
||||
companyId,
|
||||
number: `KR-${randomUUID()}`,
|
||||
creditedInvoiceId: originalId,
|
||||
}),
|
||||
).rejects.toThrow(/duplicate|unique/i)
|
||||
})
|
||||
|
||||
it('persists the completion marker and installs a unique posted-source guard', async () => {
|
||||
const { userId, companyId } = await seedCompany()
|
||||
const invoiceId = await insertInvoice({
|
||||
userId,
|
||||
companyId,
|
||||
number: `KR-${randomUUID()}`,
|
||||
creationComplete: false,
|
||||
})
|
||||
|
||||
const invoice = await getPool().query<{ creation_complete: boolean }>(
|
||||
`SELECT creation_complete FROM public.invoices WHERE id = $1`,
|
||||
[invoiceId],
|
||||
)
|
||||
expect(invoice.rows[0].creation_complete).toBe(false)
|
||||
|
||||
const index = await getPool().query<{ indexdef: string }>(
|
||||
`SELECT indexdef FROM pg_indexes
|
||||
WHERE schemaname = 'public' AND indexname = 'uq_posted_credit_note_journal_source'`,
|
||||
)
|
||||
expect(index.rows).toHaveLength(1)
|
||||
expect(index.rows[0].indexdef).toMatch(/UNIQUE/)
|
||||
expect(index.rows[0].indexdef).toMatch(/source_type.*credit_note/)
|
||||
expect(index.rows[0].indexdef).toMatch(/status.*posted/)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user