Provisions-Manager 95: Import, Händler-DB, Auszahlungsregeln, Excel-Export, Win95-Oberfläche, Wine-Build

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-11-02 09:24:39 +01:00
commit da2e3c9ba1
25 files changed
+3366

No files matched your search

+123
View File
@@ -0,0 +1,123 @@
"""Export der Händler-Abrechnung als Excel – Aufbau wie „so soll es zum schluss ausschauen.xlsx“.
Spalten: A Zeitraum | B Provisionsart | C Unterhändler-/VO-Nr | D Datum | E Rufnummer | F IMEI/SN |
G Gerät | H Vorname | I Name | J Tarif | K Betrag (Auszahlung) | L Netz | M Dienst | N Aktionscode
Sortierung: Zeitraum → Provisionsart → Datum (sonst Reihenfolge der Abrechnung).
"""
from __future__ import annotations
import re
from datetime import date
from pathlib import Path
from provtool.importer import ANBIETER
from provtool.money import to_euro
SPALTEN = [
("Zeitraum", 20), ("Provisionsart", 38), ("Unterhändler", 11), ("Datum", 11), ("Rufnummer", 13),
("IMEI/SN", 16), ("Gerät", 16), ("Vorname", 14), ("Name", 30), ("Tarif", 34), ("Betrag", 12),
("Netz", 7), ("Dienst", 22), ("Aktionscode", 12),
]
EURO_FORMAT = '_-* #,##0.00\\ "€"_-;\\-* #,##0.00\\ "€"_-;_-* "-"??\\ "€"_-;_-@_-'
def sortiert(positionen: list[dict]) -> list[dict]:
return sorted(positionen, key=lambda p: (p.get("zeitraum") or "", p.get("provisionsart") or "",
p.get("datum") or "", p.get("reihenfolge") or 0))
def _zahl_oder_text(s: str):
"""Rufnummern/IMEI als Zahl (Format „0“) wie in der Vorlage – führende Nullen bleiben als Text erhalten."""
s = (s or "").strip()
if s.isdigit() and not s.startswith("0") and len(s) <= 15:
return int(s)
return s or None
def dateiname(text: str) -> str:
return re.sub(r'[<>:"/\\|?*\x00-\x1f]', "_", text).strip(" .") or "Export"
def schreibe_xlsx(ziel: str | Path, positionen: list[dict], *, kopfzeile: bool = False, summe: bool = True,
blattname: str = "Tabelle1") -> Path:
import openpyxl
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter
ziel = Path(ziel)
wb = openpyxl.Workbook()
ws = wb.active
ws.title = blattname[:31] or "Tabelle1"
font = Font(name="Aptos Narrow", size=10)
bold = Font(name="Aptos Narrow", size=10, bold=True)
zeile = 1
if kopfzeile:
for c, (titel, _) in enumerate(SPALTEN, start=1):
ws.cell(zeile, c, titel).font = bold
ws.freeze_panes = "A2"
zeile += 1
erste = zeile
for p in sortiert(positionen):
werte = [
p.get("zeitraum") or None,
p.get("provisionsart") or None,
_zahl_oder_text(p.get("kennung")),
date.fromisoformat(p["datum"]) if p.get("datum") else None,
_zahl_oder_text(p.get("rufnummer")),
_zahl_oder_text(p.get("imei")),
p.get("geraet") or None,
p.get("vorname") or None,
p.get("name") or None,
p.get("tarif") or None,
to_euro(p["betrag"]),
p.get("netz") or None,
p.get("dienst") or None,
p.get("aktionscode") or None,
]
for c, v in enumerate(werte, start=1):
cell = ws.cell(zeile, c, v)
cell.font = font
ws.cell(zeile, 4).number_format = "DD.MM.YYYY"
ws.cell(zeile, 5).number_format = "0"
ws.cell(zeile, 6).number_format = "0"
ws.cell(zeile, 11).number_format = EURO_FORMAT
zeile += 1
letzte = zeile - 1
if summe and letzte >= erste:
s = ws.cell(zeile + 1, 11, f"=SUM(K{erste}:K{letzte})")
s.number_format = EURO_FORMAT
s.font = bold
ws.cell(zeile + 1, 10, "Summe").font = bold
for c, (_, breite) in enumerate(SPALTEN, start=1):
ws.column_dimensions[get_column_letter(c)].width = breite
ws.page_setup.orientation = "landscape"
ws.page_setup.fitToWidth = 1
ws.page_setup.fitToHeight = 0
ws.sheet_properties.pageSetUpPr.fitToPage = True
ziel.parent.mkdir(parents=True, exist_ok=True)
wb.save(ziel)
return ziel
def exportiere_haendler(ordner: str | Path, haendler_name: str, positionen: list[dict], titel: str, *,
pro_anbieter: bool = True, kopfzeile: bool = False, summe: bool = True) -> list[Path]:
"""Schreibt eine (oder je Anbieter eine) Datei für einen Händler. Gibt die erzeugten Pfade zurück."""
ordner = Path(ordner)
if not positionen:
return []
gruppen: dict[str, list[dict]] = {}
if pro_anbieter:
for p in positionen:
gruppen.setdefault(p["anbieter"], []).append(p)
else:
gruppen[""] = positionen
out = []
for anbieter, pos in sorted(gruppen.items()):
teile = [haendler_name] + ([ANBIETER.get(anbieter, anbieter)] if anbieter else []) + [titel]
name = dateiname(" - ".join(t for t in teile if t)) + ".xlsx"
out.append(schreibe_xlsx(ordner / name, pos, kopfzeile=kopfzeile, summe=summe))
return out