diff --git a/msp/fixtures/supplier_import_profile.json b/msp/fixtures/supplier_import_profile.json index 31ed678..673960c 100644 --- a/msp/fixtures/supplier_import_profile.json +++ b/msp/fixtures/supplier_import_profile.json @@ -22,6 +22,8 @@ "supplier": "SUPP-00887", "title_prefix": "Abrechnung Microsoft Abo ", "title_prefix_credit_note": "Gutschrift Microsoft Abo ", + "title_template": "{prefix} {period}", + "title_template_credit_note": "{prefix} {period}", "vendor": "Microsoft" } ] \ No newline at end of file diff --git a/msp/importers/builder.py b/msp/importers/builder.py index 8777a2f..4391bcb 100644 --- a/msp/importers/builder.py +++ b/msp/importers/builder.py @@ -113,16 +113,15 @@ class DocumentBuilder: """Erzeugt das Grundgerüst (ohne Zeilen) der Sales Invoice / Delivery Note.""" profile = self.profile period = self._format_period_label(first) - title_prefix = profile.title_prefix_credit_note if is_return else profile.title_prefix - title = f"{(title_prefix or '').strip()} {period}".strip() + posting_date = self._coerce_german_date(first.raw.get("DATUM") or frappe.utils.today()) + title = self._render_title(customer, period, posting_date, is_return=is_return) if self.output_mode == "Sales Invoice": doc = frappe.new_doc("Sales Invoice") doc.customer = customer doc.company = profile.default_company doc.currency = frappe.db.get_value("Company", profile.default_company, "default_currency") or "EUR" - doc.posting_date = first.raw.get("DATUM") or frappe.utils.today() - doc.posting_date = self._coerce_german_date(doc.posting_date) + doc.posting_date = posting_date doc.set_posting_time = 1 if profile.default_cost_center: doc.cost_center = profile.default_cost_center @@ -147,7 +146,7 @@ class DocumentBuilder: doc = frappe.new_doc("Delivery Note") doc.customer = customer doc.company = profile.default_company - doc.posting_date = self._coerce_german_date(first.raw.get("DATUM") or frappe.utils.today()) + doc.posting_date = posting_date doc.set_posting_time = 1 if profile.default_cost_center: doc.cost_center = profile.default_cost_center @@ -312,6 +311,42 @@ class DocumentBuilder: return invdate.strftime("%m.%Y") return _d.today().strftime("%m.%Y") + def _render_title(self, customer: str, period: str, posting_date, *, is_return: bool) -> str: + """Rendert den Beleg-Titel aus dem konfigurierten Template (mit Fallback). + + Platzhalter: {prefix}, {period}, {customer}, {customer_name}, + {vendor}, {posting_date}, {document_type}. + """ + profile = self.profile + template = (profile.title_template_credit_note if is_return else profile.title_template) or "" + prefix = (profile.title_prefix_credit_note if is_return else profile.title_prefix) or "" + template = template.strip() + if not template: + # Backwards-compat: bisheriges Verhalten "{prefix} {period}". + return f"{prefix.strip()} {period}".strip() + + customer_name = frappe.db.get_value("Customer", customer, "customer_name") or customer + try: + rendered = template.format( + prefix=prefix.strip(), + period=period, + customer=customer, + customer_name=customer_name, + vendor=profile.vendor or "", + posting_date=posting_date.strftime("%d.%m.%Y") if hasattr(posting_date, "strftime") else str(posting_date), + document_type=self.output_mode or "", + ) + except (KeyError, IndexError) as e: + # Unbekannter Platzhalter — robust auf Fallback zurückfallen, aber dem Run + # trotzdem einen Hinweis mitgeben, indem wir den Profilfehler in die Logs schreiben. + frappe.log_error( + f"Title-Template enthält ungültigen Platzhalter ({e}); fällt auf '{{prefix}} {{period}}' zurück.", + "Supplier Import Builder", + ) + rendered = f"{prefix.strip()} {period}".strip() + # Mehrfach-Whitespace zusammenfassen + return " ".join(rendered.split()) + @staticmethod def _copy_tax_template_rows(doc, template_name: str) -> None: """Kopiert die Zeilen des Sales-Taxes-and-Charges-Templates explizit in doc.taxes. diff --git a/msp/msp/doctype/supplier_import_profile/supplier_import_profile.json b/msp/msp/doctype/supplier_import_profile/supplier_import_profile.json index 446b7d6..1848a99 100644 --- a/msp/msp/doctype/supplier_import_profile/supplier_import_profile.json +++ b/msp/msp/doctype/supplier_import_profile/supplier_import_profile.json @@ -29,6 +29,8 @@ "text_section", "title_prefix", "title_prefix_credit_note", + "title_template", + "title_template_credit_note", "intro_text_invoice", "intro_text_credit_note", "fallback_section", @@ -170,6 +172,18 @@ "fieldtype": "Data", "label": "Title Prefix (Credit Note)" }, + { + "fieldname": "title_template", + "fieldtype": "Data", + "label": "Title Template (Invoice)", + "description": "Optionales Format-String für den Beleg-Titel. Platzhalter: {prefix}, {period}, {customer}, {customer_name}, {vendor}, {posting_date}, {document_type}. Leer = '{prefix} {period}'." + }, + { + "fieldname": "title_template_credit_note", + "fieldtype": "Data", + "label": "Title Template (Credit Note)", + "description": "Wie 'Title Template (Invoice)', nur für Gutschriften. Leer = '{prefix} {period}'." + }, { "fieldname": "intro_text_invoice", "fieldtype": "Text Editor",