From ea4905ef8ac54127cebae56bede19d66ed649add Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 2 Mar 2026 00:35:44 +0100 Subject: [PATCH] fix: vendor DB lookup after regex/AI extraction v2.2.20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extract_vendor_suggestion now matches found CVR/domain/name against vendors table for ALL code paths (not just fast path): - CVR match → score 100 - Domain match → score 80 - Email-domain match → score 75 - Name substring → score 60 Frontend auto-links when match_score >= 80 (was only pdf_extraction source). Shows score reason: CVR-match/domæne-match/navne-match in success toast. Also: saves newly found CVR to extracted_vendor_cvr so fast path works on subsequent calls for old emails too. --- VERSION | 2 +- app/emails/backend/router.py | 60 +++++++++++++++++++++++++++++++++ app/emails/frontend/emails.html | 5 +-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index ef93bcc..0c2c783 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.19 +2.2.20 diff --git a/app/emails/backend/router.py b/app/emails/backend/router.py index cba785a..2e3cf79 100644 --- a/app/emails/backend/router.py +++ b/app/emails/backend/router.py @@ -652,6 +652,7 @@ async def extract_vendor_suggestion(email_id: int): "domain": None, "source": "pdf_extraction", "vendor_id": vendor_row.get('id') if vendor_row else None, + "match_score": 100 if vendor_row else 0, "confidence": 0.95, } logger.info( @@ -890,6 +891,65 @@ JSON:""" if em_domain in PLATFORM_DOMAINS: suggestion['email'] = None + # ── Vendor DB-opslag (gælder alle stier undtagen fast path ovenfor) ─── + # Prøv at matche det udtrukkede CVR/navn/domæne mod vendors-tabellen. + vendor_id = None + match_score = 0 + + all_vendors = execute_query( + "SELECT id, name, cvr_number, email, domain FROM vendors WHERE is_active = true", + None + ) or [] + + cvr_found = suggestion.get('cvr_number') + name_found = (suggestion.get('name') or '').lower().strip() + domain_found = (suggestion.get('domain') or '').lower().strip() + + for v in all_vendors: + score = 0 + # CVR-match er stærkest (100 point) + if cvr_found and v.get('cvr_number') and str(cvr_found).strip() == str(v['cvr_number']).strip(): + score = 100 + # Domæne-match (80 point) + elif domain_found and v.get('domain') and domain_found == v['domain'].lower().strip(): + score = 80 + # Email-domæne-match (75 point) + elif domain_found and v.get('email') and '@' in (v['email'] or ''): + vdom = v['email'].split('@')[1].lower() + if domain_found == vdom: + score = 75 + # Navne-match: vendor-navn er substring af fundne navn eller omvendt (60 point) + elif name_found and v.get('name'): + vname = v['name'].lower().strip() + if vname and (vname in name_found or name_found in vname): + score = 60 + + if score > match_score: + match_score = score + vendor_id = v['id'] + suggestion['name'] = suggestion.get('name') or v['name'] + if not cvr_found and v.get('cvr_number'): + suggestion['cvr_number'] = v['cvr_number'] + + if vendor_id: + suggestion['vendor_id'] = vendor_id + suggestion['match_score'] = match_score + logger.info(f"✅ Vendor match for email {email_id}: vendor_id={vendor_id}, score={match_score}") + + # Gem CVR på emailen så fast path virker næste gang + if cvr_found and not email.get('extracted_vendor_cvr'): + try: + execute_update( + "UPDATE email_messages SET extracted_vendor_cvr = %s, extracted_vendor_name = %s WHERE id = %s", + (cvr_found, suggestion.get('name'), email_id) + ) + except Exception: + pass + else: + suggestion['vendor_id'] = None + suggestion['match_score'] = 0 + logger.info(f"⚠️ Ingen vendor match for email {email_id}: CVR={cvr_found}, navn={name_found}") + return suggestion except HTTPException: diff --git a/app/emails/frontend/emails.html b/app/emails/frontend/emails.html index d7f74d8..7bbb773 100644 --- a/app/emails/frontend/emails.html +++ b/app/emails/frontend/emails.html @@ -2349,7 +2349,7 @@ async function quickCreateVendor(emailId, senderName, senderEmail) { const s = await resp.json(); // ── Hurtig genvej: leverandøren kendes allerede (vendor_id fra PDF) ── - if (s.vendor_id && s.source === 'pdf_extraction') { + if (s.vendor_id && (s.source === 'pdf_extraction' || s.match_score >= 80)) { bootstrap.Modal.getInstance(document.getElementById('quickCreateVendorModal')).hide(); // Auto-link direkte const linkResp = await fetch(`/api/v1/emails/${emailId}/link`, { @@ -2358,7 +2358,8 @@ async function quickCreateVendor(emailId, senderName, senderEmail) { body: JSON.stringify({ supplier_id: s.vendor_id }) }); if (linkResp.ok) { - showSuccess(`Leverandør "${s.name || s.cvr_number}" auto-linket fra faktura-PDF`); + const scoreLabel = s.match_score >= 100 ? 'CVR-match' : s.match_score >= 80 ? 'domæne-match' : 'navne-match'; + showSuccess(`Leverandør "${s.name || s.cvr_number}" auto-linket (${scoreLabel})`); loadEmailDetail(parseInt(emailId)); } else { showError('Auto-link fejlede — åbner formularen');