fix(msp): Gutschriften nicht gegen reguläre Rechnungen deduplizieren + Title nach Insert setzen

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.
This commit is contained in:
David Malinowski
2026-04-14 17:32:57 +02:00
parent d165190b87
commit bcafb78fa7
2 changed files with 13 additions and 3 deletions
+1 -1
View File
@@ -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",
+12 -2
View File
@@ -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)"}