Feat/voucher docs (#664)
* feat: implement inbox document picker and linking functionality * feat: implement self-billing invoice functionality - Added support for registering self-billed invoices received from customers. - Updated the invoice schema to include fields for self-billing metadata such as `is_self_billed`, `external_invoice_number`, `self_billing_agreement_ref`, and `received_date`. - Created API route for handling self-billed invoice submissions, including validation and error handling. - Implemented database migrations to add necessary columns and constraints for self-billing invoices. - Developed tests to ensure correct behavior of self-billing invoice creation and validation rules. - Updated Swedish localization files to include new terms related to self-billing. * feat: enforce SIE import requirement for non-Fortnox providers in migration process * feat: streamline invoice processing and enhance error logging across APIs
This commit is contained in:
@@ -86,4 +86,34 @@ describe('ensureInvoiceNumber', () => {
|
||||
ensureInvoiceNumber(supabase as never, 'company-1', invoice)
|
||||
).rejects.toThrow('no value returned')
|
||||
})
|
||||
|
||||
it('returns the external number for a self-billed invoice without touching the RPC', async () => {
|
||||
// A received self-billing invoice carries the counterparty's number; we must
|
||||
// never consume our own löpnummerserie (BFL 5 kap 6§).
|
||||
const invoice = {
|
||||
id: 'inv-1',
|
||||
invoice_number: null,
|
||||
is_self_billed: true,
|
||||
external_invoice_number: 'KUND-55012',
|
||||
}
|
||||
|
||||
const result = await ensureInvoiceNumber(supabase as never, 'company-1', invoice)
|
||||
|
||||
expect(result).toBe('KUND-55012')
|
||||
expect(supabase.rpc).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('throws when a self-billed invoice is missing the external number', async () => {
|
||||
const invoice = {
|
||||
id: 'inv-1',
|
||||
invoice_number: null,
|
||||
is_self_billed: true,
|
||||
external_invoice_number: null,
|
||||
}
|
||||
|
||||
await expect(
|
||||
ensureInvoiceNumber(supabase as never, 'company-1', invoice)
|
||||
).rejects.toThrow('missing external_invoice_number')
|
||||
expect(supabase.rpc).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,3 +3,16 @@ export const INVOICE_NUMBER_DRAFT_LABEL = '(Utkast)'
|
||||
export function invoiceNumberDisplay(value: string | null | undefined): string {
|
||||
return value ?? INVOICE_NUMBER_DRAFT_LABEL
|
||||
}
|
||||
|
||||
/**
|
||||
* The number to show for an invoice. Self-billing invoices we received carry
|
||||
* the counterparty's number in `external_invoice_number` (our own
|
||||
* `invoice_number` is null by design), so fall back to it before the draft
|
||||
* label.
|
||||
*/
|
||||
export function invoiceDisplayNumber(invoice: {
|
||||
invoice_number?: string | null
|
||||
external_invoice_number?: string | null
|
||||
}): string {
|
||||
return invoice.invoice_number ?? invoice.external_invoice_number ?? INVOICE_NUMBER_DRAFT_LABEL
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import type { Invoice, InvoiceDocumentType } from '@/types'
|
||||
type InvoiceShape = Pick<Invoice, 'id' | 'invoice_number'> & {
|
||||
invoice_number: string | null
|
||||
document_type?: InvoiceDocumentType | null
|
||||
is_self_billed?: boolean | null
|
||||
external_invoice_number?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,6 +26,17 @@ export async function ensureInvoiceNumber(
|
||||
return invoice.invoice_number
|
||||
}
|
||||
|
||||
// Self-billing invoices we received carry the COUNTERPARTY's number; we must
|
||||
// never consume our own löpnummerserie for them (BFL 5 kap 6§). The DB
|
||||
// constraint invoices_self_billed_numbering guarantees the external number is
|
||||
// present, but guard here so a future caller can't silently mint an F-number.
|
||||
if (invoice.is_self_billed) {
|
||||
if (!invoice.external_invoice_number) {
|
||||
throw new Error('Self-billed invoice is missing external_invoice_number')
|
||||
}
|
||||
return invoice.external_invoice_number
|
||||
}
|
||||
|
||||
const { data: assigned, error: rpcError } = await supabase.rpc('generate_invoice_number', {
|
||||
p_company_id: companyId,
|
||||
p_invoice_id: invoice.id,
|
||||
|
||||
Reference in New Issue
Block a user