From bcafb78fa7726db1fd9493b312205ec67fdea386 Mon Sep 17 00:00:00 2001 From: David Malinowski Date: Tue, 14 Apr 2026 17:32:57 +0200 Subject: [PATCH] =?UTF-8?q?fix(msp):=20Gutschriften=20nicht=20gegen=20regu?= =?UTF-8?q?l=C3=A4re=20Rechnungen=20deduplizieren=20+=20Title=20nach=20Ins?= =?UTF-8?q?ert=20setzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zwei Bugs beim ersten realen Gutschrift-Test (ADN-CSV vom 16.10.2025, 2 Zeilen mit negativer Menge): 1. Dedup-Fehler — Gutschriftzeilen wurden als „bereits importiert" geskippt, weil die Prüfung (subscription_external_id, period_start, period_end) auch bei der regulären Rechnung desselben Monats matchte. Gutschriften sind aber inhaltlich Korrekturen, keine Dubletten. Fix: Dedup greift nur bei regulären Rechnungszeilen, nicht bei Gutschriftzeilen (qty<0, amount<0 oder document_type=Gutschrift). 2. Title-Override — ERPNext setzt bei Sales-Invoice-Insert den Title aus der autoname-Schablone auf ``{customer_name}``, wodurch unser „Abrechnung Microsoft Abo MM.JJJJ" verloren ging. Fix: nach doc.insert() den Title direkt per db.set_value wieder einsetzen (umgeht Auto-Title-Logik). Ergebnis der Validierung (Oktober 2025, das einzige Monat mit Gutschrift- Zeilen in den verfügbaren CSVs): - SINV-271450: CUST-21844, -2× MAPID-20763063, Netto -11,76 / Brutto -13,99 - SINV-271455: CUST-21865, -5× ITEM-00052, Netto -123,60 / Brutto -147,08 - Beide mit is_return=1 und korrektem Titel „Gutschrift Microsoft Abo 10.2025" Nebenbefund zu adnconnect: historische Gutschriftbehandlung dort fehlerhaft — die -2-Zeile von CUST-21844 landete in der Haupt-SI mit Rabatt=100 % (Gutschrift-Effekt verloren), die -5-Zeile von CUST-21865 wurde komplett verschluckt. Unser Importer deckt das jetzt sauber ab. --- msp/importers/_batchtest.py | 2 +- msp/importers/builder.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/msp/importers/_batchtest.py b/msp/importers/_batchtest.py index 6adaa54..587d6c1 100644 --- a/msp/importers/_batchtest.py +++ b/msp/importers/_batchtest.py @@ -10,7 +10,7 @@ from msp.importers._runner import run as run_one CSV_SEQUENCE = [ - "/private/files/433148_Rechnungen_17_10_2025.csv", + "/private/files/433148_Rechnungen_16_10_2025.csv", "/private/files/433148_Rechnungen_17_11_2025.csv", "/private/files/433148_Rechnungen_18_12_2025.csv", "/private/files/433148_Rechnungen_16_01_2026.csv", diff --git a/msp/importers/builder.py b/msp/importers/builder.py index 1a7b85f..8777a2f 100644 --- a/msp/importers/builder.py +++ b/msp/importers/builder.py @@ -98,6 +98,11 @@ class DocumentBuilder: o["error"] = str(e) return + # Title nach Insert festnageln — ERPNext setzt ihn sonst über + # title_field/autoname auf "{customer_name}" zurück. + if doc.title and "{" not in doc.title: + frappe.db.set_value(doc.doctype, doc.name, "title", doc.title) + result.created_documents.append((doc.doctype, doc.name)) # Subscription-Events erst nach erfolgreichem Insert, damit wir die @@ -162,8 +167,13 @@ class DocumentBuilder: "error": f"Item-Lookup {reason}: vendor_product_id={row.vendor_product_id}"} # Deduplizierung: bereits Event für diese Subscription+Period vorhanden? - if row.subscription_external_id and event_exists_for_period( - row.subscription_external_id, row.period_start, row.period_end): + # Gutschriftzeilen (qty<0 oder amount<0) laufen IMMER durch — sie sind inhaltlich + # eine Korrektur der bereits erfassten Rechnung, nie eine Dublette. + is_credit_row = (row.qty or 0) < 0 or (row.amount or 0) < 0 or row.document_type == "Gutschrift" + if (not is_credit_row + and row.subscription_external_id + and event_exists_for_period( + row.subscription_external_id, row.period_start, row.period_end)): return {"row": row, "status": "Skipped", "error": "Bereits importiert (Subscription+Period existieren)"}