Files
provisionsmanager95/provtool/export.py
T
adminandClaude Opus 5.5 dcb1d0378c Eigene Kennungen ausblenden, Win95-Menüs, verschiebbares Hauptfenster bei offenem Dialog u. a.
Neu:
- Eigene Kennungen (eigene Filiale) ausblenden: nicht angezeigt, nicht mitgerechnet, nicht
  exportiert; per Rechtsklick, Händler-Menü oder Import-Bericht; Händler-Zuordnung hat Vorrang
- Menüs im Win95-Stil: wechseln beim Überfahren der Menüleiste, Tastatur, Kontextmenü gleich
- Dialoge sperren nur den Inhalt: Hauptfenster bleibt verschiebbar/maximierbar; Dialoge haben
  eigenen Taskleisten-Eintrag; Klick auf gesperrten Inhalt holt den Dialog nach vorn (blinkt)
- Trennlinie zwischen Händler- und Positionsliste sichtbar und ziehbar, Position wird gemerkt
- Strg+Rücktaste / Strg+Entf löschen ganze Wörter in allen Eingabefeldern

Behoben:
- Gedrückte Knöpfe zeigten keine Beschriftung (activeForeground gehört in Tk zur Klasse Background)
- Tipp des Tages: Text lief aus dem sichtbaren Bereich
- Größenänderung: breiterer Griffbereich mit Ecken, Zeiger wird zuverlässig zurückgesetzt,
  flüssiger; Verschieben über Windows selbst (inkl. Andocken), Minimieren über Windows
- Zeichen, die MS Sans Serif nicht hat (…, ▸, →, •), durch Win95-übliche ersetzt
- Menü/Tooltip/Dialog: Größe+Position zusammen setzen (Tk ignoriert sonst die Position)
- Suche reagiert erst nach kurzer Tipp-Pause (flüssigeres Tippen)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 12:58:18 +01:00

124 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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