feat(mcp): add invoice_date_override to create_supplier_invoice_from_inbox (#848)
Unblocks inbox items where OCR missed the invoice date: priority is override, then extracted invoiceDate, then the existing hard error. Both date overrides now reject non-ISO input at staging time instead of failing opaquely at booking. Includes dry_run and validation tests.
This commit is contained in:
@@ -491,6 +491,51 @@ describe('gnubok_create_supplier_invoice_from_inbox: execute', () => {
|
||||
expect('dimensions' in params.items[0]).toBe(false)
|
||||
})
|
||||
|
||||
it('invoice_date_override rescues an inbox item with no extracted invoiceDate', async () => {
|
||||
const extractedNoDate = {
|
||||
...baseExtracted,
|
||||
invoice: { ...baseExtracted.invoice, invoiceDate: null },
|
||||
}
|
||||
const supabase = makeMock({
|
||||
inbox: {
|
||||
id: 'inbox-10',
|
||||
status: 'received',
|
||||
extracted_data: extractedNoDate,
|
||||
matched_supplier_id: 'supplier-1',
|
||||
created_supplier_invoice_id: null,
|
||||
document_id: 'doc-10',
|
||||
},
|
||||
})
|
||||
const tool = tools.find((t) => t.name === 'gnubok_create_supplier_invoice_from_inbox')!
|
||||
const result = (await tool.execute(
|
||||
{ inbox_item_id: 'inbox-10', dry_run: true, invoice_date_override: '2025-07-15' },
|
||||
'company-1', 'user-1', supabase,
|
||||
)) as { dry_run: boolean; preview: { invoice_date: string } }
|
||||
|
||||
expect(result.dry_run).toBe(true)
|
||||
expect(result.preview.invoice_date).toBe('2025-07-15')
|
||||
})
|
||||
|
||||
it('rejects a non-ISO invoice_date_override before staging', async () => {
|
||||
const supabase = makeMock({
|
||||
inbox: {
|
||||
id: 'inbox-11',
|
||||
status: 'received',
|
||||
extracted_data: baseExtracted,
|
||||
matched_supplier_id: 'supplier-1',
|
||||
created_supplier_invoice_id: null,
|
||||
document_id: 'doc-11',
|
||||
},
|
||||
})
|
||||
const tool = tools.find((t) => t.name === 'gnubok_create_supplier_invoice_from_inbox')!
|
||||
await expect(
|
||||
tool.execute(
|
||||
{ inbox_item_id: 'inbox-11', dry_run: true, invoice_date_override: '15/07/2025' },
|
||||
'company-1', 'user-1', supabase,
|
||||
),
|
||||
).rejects.toThrow(/invoice_date_override must be an ISO date/)
|
||||
})
|
||||
|
||||
it('throws when extracted_data is missing', async () => {
|
||||
const supabase = makeMock({
|
||||
inbox: {
|
||||
|
||||
@@ -7572,6 +7572,7 @@ export const tools: McpTool[] = [
|
||||
inbox_item_id: { type: 'string', description: 'UUID of the inbox item to convert' },
|
||||
supplier_id_override: { type: 'string', description: 'Force this supplier UUID instead of the matched/extracted one' },
|
||||
vat_treatment_override: { type: 'string', enum: ['standard_25', 'reduced_12', 'reduced_6', 'reverse_charge', 'export', 'exempt'], description: 'Override extracted VAT treatment' },
|
||||
invoice_date_override: { type: 'string', description: 'Override extracted invoice date (YYYY-MM-DD). Use when OCR misses the date.' },
|
||||
due_date_override: { type: 'string', description: 'Override extracted due date (YYYY-MM-DD)' },
|
||||
line_overrides: {
|
||||
type: 'array',
|
||||
@@ -7756,7 +7757,13 @@ export const tools: McpTool[] = [
|
||||
|
||||
// Assemble core invoice fields
|
||||
const currency = (invoiceExt?.currency as string) || 'SEK'
|
||||
const invoiceDate = (invoiceExt?.invoiceDate as string) || null
|
||||
for (const key of ['invoice_date_override', 'due_date_override'] as const) {
|
||||
const value = args[key] as string | undefined
|
||||
if (value !== undefined && !ISO_DATE_RE.test(value)) {
|
||||
throw new Error(`${key} must be an ISO date (YYYY-MM-DD), got "${value}"`)
|
||||
}
|
||||
}
|
||||
const invoiceDate = (args.invoice_date_override as string | undefined) ?? (invoiceExt?.invoiceDate as string) ?? null
|
||||
const dueDate = (args.due_date_override as string | undefined) ?? (invoiceExt?.dueDate as string | undefined) ?? null
|
||||
const supplierInvoiceNumber = (invoiceExt?.invoiceNumber as string) || ''
|
||||
if (!invoiceDate) throw new Error('Extracted invoice has no invoice date')
|
||||
|
||||
Reference in New Issue
Block a user