fix: vendor pre-fill bruger server-normaliseret llm_data + operator precedence fix v2.2.28

This commit is contained in:
Christian 2026-03-02 09:33:50 +01:00
parent 14ccd5accf
commit d561a063f6
2 changed files with 57 additions and 28 deletions

View File

@ -527,6 +527,28 @@ async def get_file_extracted_data(file_id: int):
due_date_value = llm_json_data.get('due_date') 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) # Use invoice_number from LLM JSON (works for both AI and template extraction)
llm_data = { llm_data = {
"invoice_number": llm_json_data.get('invoice_number'), "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, "total_amount": float(total_amount_value) if total_amount_value else None,
"currency": llm_json_data.get('currency') or 'DKK', "currency": llm_json_data.get('currency') or 'DKK',
"document_type": llm_json_data.get('document_type'), "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 "lines": formatted_lines
} }
elif extraction: 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, "total_amount": float(extraction.get('total_amount')) if extraction.get('total_amount') else None,
"currency": extraction.get('currency') or 'DKK', "currency": extraction.get('currency') or 'DKK',
"document_type": extraction.get('document_type'), "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 "lines": formatted_lines
} }

View File

@ -2119,40 +2119,40 @@ async function openQuickVendorCreate(fileId, filename) {
const resp = await fetch(`/api/v1/supplier-invoices/files/${fileId}/extracted-data`); const resp = await fetch(`/api/v1/supplier-invoices/files/${fileId}/extracted-data`);
if (resp.ok) { if (resp.ok) {
const data = await resp.json(); 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 || {}; const ext = data.extraction || {};
// llm_response_json kan være allerede-parsed objekt (FastAPI/JSONB) eller string // llm_response_json kan være objekt (JSONB) eller string parse sikkert som fallback
let ai = {}; let rawAi = {};
const rawLlm = ext.llm_response_json; const rawLlm = ext.llm_response_json;
if (rawLlm) { if (rawLlm) {
if (typeof rawLlm === 'string') { 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') { } else if (typeof rawLlm === 'object') {
ai = rawLlm; // allerede parsed af browser rawAi = rawLlm;
} }
} }
// Vendor navn: prøv extraction tabel først, derefter LLM JSON // Prioriter llm_data (server-normaliseret) → extraction kolonne → raw AI JSON
const name = ext.vendor_name || ai.vendor_name || ai.supplier_name || ''; const name = ld.vendor_name || ext.vendor_name || rawAi.vendor_name || rawAi.issuer || '';
// Vendor CVR: strip "DK" prefix const cvr = (ld.vendor_cvr || ext.vendor_cvr || rawAi.vendor_cvr || rawAi.vendor_vat || '').toString().replace(/^DK/i, '').trim();
const cvr = (ext.vendor_cvr || ai.vendor_cvr || ai.supplier_cvr || '').toString().replace(/^DK/i, '').trim(); const email = ld.vendor_email || rawAi.vendor_email || rawAi.supplier_email || '';
// Vendor email const addr = ld.vendor_address || rawAi.vendor_address || rawAi.supplier_address || rawAi.vendor_street || '';
const email = ai.vendor_email || ai.supplier_email || ''; const postal = ld.vendor_postal_code || rawAi.vendor_postal_code || rawAi.postal_code || '';
// Vendor adresse: prøv samlet adresse eller separate felter const city = ld.vendor_city || rawAi.vendor_city || rawAi.city || '';
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(', ') || '';
if (name) document.getElementById('qvName').value = name; if (name) document.getElementById('qvName').value = name;
if (cvr) document.getElementById('qvCVR').value = cvr; if (cvr) document.getElementById('qvCVR').value = cvr;
if (email) document.getElementById('qvEmail').value = email; if (email) document.getElementById('qvEmail').value = email;
if (addr && addr.trim()) { if (addr) {
// Forsøg at splitte adresse i gade / postnr / by // Forsøg at splitte samlet adresse i gade / postnr / by
const parts = addr.split(/,|\n/).map(s => s.trim()).filter(Boolean); const parts = addr.split(/,|\n/).map(s => s.trim()).filter(Boolean);
if (parts.length >= 1) document.getElementById('qvAddress').value = parts[0]; 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 postalCity = parts[parts.length - 1];
const m = postalCity.match(/^(\d{4})\s+(.+)$/); const m = postalCity.match(/^(\d{4})\s+(.+)$/);
if (m) { if (m) {
@ -2162,21 +2162,16 @@ async function openQuickVendorCreate(fileId, filename) {
document.getElementById('qvCity').value = postalCity; 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) { if (!name && !cvr) {
const statusEl = document.getElementById('qvStatusAlert'); const statusEl = document.getElementById('qvStatusAlert');
statusEl.className = 'alert alert-warning py-2 small'; 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) { } catch(e) {