From d561a063f6be20c840076aede03296ac80912da5 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 2 Mar 2026 09:33:50 +0100 Subject: [PATCH] fix: vendor pre-fill bruger server-normaliseret llm_data + operator precedence fix v2.2.28 --- app/billing/backend/supplier_invoices.py | 34 ++++++++++++++ app/billing/frontend/supplier_invoices.html | 51 ++++++++++----------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/app/billing/backend/supplier_invoices.py b/app/billing/backend/supplier_invoices.py index e1418bb..a126df6 100644 --- a/app/billing/backend/supplier_invoices.py +++ b/app/billing/backend/supplier_invoices.py @@ -527,6 +527,28 @@ async def get_file_extracted_data(file_id: int): due_date_value = llm_json_data.get('due_date') + # Vendor name: AI uses 'vendor_name', invoice2data uses 'issuer' + vendor_name_val = ( + llm_json_data.get('vendor_name') or + llm_json_data.get('issuer') or + (extraction.get('vendor_name') if extraction else None) + ) + # Vendor CVR: AI uses 'vendor_cvr', invoice2data uses 'vendor_vat' + vendor_cvr_val = ( + llm_json_data.get('vendor_cvr') or + llm_json_data.get('vendor_vat') or + (extraction.get('vendor_cvr') if extraction else None) + ) + # Vendor address: AI uses 'vendor_address', invoice2data may have separate fields + vendor_address_val = ( + llm_json_data.get('vendor_address') or + llm_json_data.get('supplier_address') or + llm_json_data.get('vendor_street') + ) + vendor_city_val = llm_json_data.get('vendor_city') or llm_json_data.get('city') + vendor_postal_val = llm_json_data.get('vendor_postal_code') or llm_json_data.get('postal_code') + vendor_email_val = llm_json_data.get('vendor_email') or llm_json_data.get('supplier_email') + # Use invoice_number from LLM JSON (works for both AI and template extraction) llm_data = { "invoice_number": llm_json_data.get('invoice_number'), @@ -535,6 +557,12 @@ async def get_file_extracted_data(file_id: int): "total_amount": float(total_amount_value) if total_amount_value else None, "currency": llm_json_data.get('currency') or 'DKK', "document_type": llm_json_data.get('document_type'), + "vendor_name": vendor_name_val, + "vendor_cvr": vendor_cvr_val, + "vendor_address": vendor_address_val, + "vendor_city": vendor_city_val, + "vendor_postal_code": vendor_postal_val, + "vendor_email": vendor_email_val, "lines": formatted_lines } elif extraction: @@ -546,6 +574,12 @@ async def get_file_extracted_data(file_id: int): "total_amount": float(extraction.get('total_amount')) if extraction.get('total_amount') else None, "currency": extraction.get('currency') or 'DKK', "document_type": extraction.get('document_type'), + "vendor_name": extraction.get('vendor_name'), + "vendor_cvr": extraction.get('vendor_cvr'), + "vendor_address": None, + "vendor_city": None, + "vendor_postal_code": None, + "vendor_email": None, "lines": formatted_lines } diff --git a/app/billing/frontend/supplier_invoices.html b/app/billing/frontend/supplier_invoices.html index 96aa56d..302b71a 100644 --- a/app/billing/frontend/supplier_invoices.html +++ b/app/billing/frontend/supplier_invoices.html @@ -2119,40 +2119,40 @@ async function openQuickVendorCreate(fileId, filename) { const resp = await fetch(`/api/v1/supplier-invoices/files/${fileId}/extracted-data`); if (resp.ok) { const data = await resp.json(); + + // Brug det server-normaliserede llm_data objekt som primær kilde + // Backend normaliserer feltnavne fra både AI (vendor_name) og invoice2data (issuer) + const ld = data.llm_data || {}; const ext = data.extraction || {}; - // llm_response_json kan være allerede-parsed objekt (FastAPI/JSONB) eller string - let ai = {}; + // llm_response_json kan være objekt (JSONB) eller string – parse sikkert som fallback + let rawAi = {}; const rawLlm = ext.llm_response_json; if (rawLlm) { if (typeof rawLlm === 'string') { - try { ai = JSON.parse(rawLlm); } catch(e) { console.warn('llm_response_json parse error:', e); } + try { rawAi = JSON.parse(rawLlm); } catch(e) {} } else if (typeof rawLlm === 'object') { - ai = rawLlm; // allerede parsed af browser + rawAi = rawLlm; } } - // Vendor navn: prøv extraction tabel først, derefter LLM JSON - const name = ext.vendor_name || ai.vendor_name || ai.supplier_name || ''; - // Vendor CVR: strip "DK" prefix - const cvr = (ext.vendor_cvr || ai.vendor_cvr || ai.supplier_cvr || '').toString().replace(/^DK/i, '').trim(); - // Vendor email - const email = ai.vendor_email || ai.supplier_email || ''; - // Vendor adresse: prøv samlet adresse eller separate felter - const addr = ai.vendor_address || ai.supplier_address || - [ai.vendor_street || ai.supplier_street, - (ai.vendor_postal_code || ai.postal_code || '') + ' ' + (ai.vendor_city || ai.city || '') - ].filter(Boolean).join(', ') || ''; + // Prioriter llm_data (server-normaliseret) → extraction kolonne → raw AI JSON + const name = ld.vendor_name || ext.vendor_name || rawAi.vendor_name || rawAi.issuer || ''; + const cvr = (ld.vendor_cvr || ext.vendor_cvr || rawAi.vendor_cvr || rawAi.vendor_vat || '').toString().replace(/^DK/i, '').trim(); + const email = ld.vendor_email || rawAi.vendor_email || rawAi.supplier_email || ''; + const addr = ld.vendor_address || rawAi.vendor_address || rawAi.supplier_address || rawAi.vendor_street || ''; + const postal = ld.vendor_postal_code || rawAi.vendor_postal_code || rawAi.postal_code || ''; + const city = ld.vendor_city || rawAi.vendor_city || rawAi.city || ''; if (name) document.getElementById('qvName').value = name; if (cvr) document.getElementById('qvCVR').value = cvr; if (email) document.getElementById('qvEmail').value = email; - if (addr && addr.trim()) { - // Forsøg at splitte adresse i gade / postnr / by + if (addr) { + // Forsøg at splitte samlet adresse i gade / postnr / by const parts = addr.split(/,|\n/).map(s => s.trim()).filter(Boolean); if (parts.length >= 1) document.getElementById('qvAddress').value = parts[0]; - if (parts.length >= 2) { + if (!postal && !city && parts.length >= 2) { const postalCity = parts[parts.length - 1]; const m = postalCity.match(/^(\d{4})\s+(.+)$/); if (m) { @@ -2162,21 +2162,16 @@ async function openQuickVendorCreate(fileId, filename) { document.getElementById('qvCity').value = postalCity; } } - } else { - // Separate felter fra AI hvis ingen samlet adresse - if (ai.vendor_street || ai.supplier_street) - document.getElementById('qvAddress').value = ai.vendor_street || ai.supplier_street || ''; - if (ai.vendor_postal_code || ai.postal_code) - document.getElementById('qvPostal').value = ai.vendor_postal_code || ai.postal_code || ''; - if (ai.vendor_city || ai.city) - document.getElementById('qvCity').value = ai.vendor_city || ai.city || ''; } + // Separate adressefelter + if (postal) document.getElementById('qvPostal').value = postal; + if (city) document.getElementById('qvCity').value = city; - // Vis status hvis ingen relevante data fundet + // Vis advarsel hvis ingen relevante data overhovedet if (!name && !cvr) { const statusEl = document.getElementById('qvStatusAlert'); statusEl.className = 'alert alert-warning py-2 small'; - statusEl.textContent = 'Ingen AI-data fundet for denne fil endnu. Kør fakturaen igennem igen (↺) og prøv derefter.'; + statusEl.textContent = 'Ingen AI-data fundet for denne fil. Kør fakturaen igennem igen (↺) og åbn panelet igen.'; } } } catch(e) {