# -*- coding: utf-8 -*- """ Excel-Import. Liest die bisherige Steuertabelle (`Beherbergungsteuer.xlsx`) ins Journal – sowohl den Altbestand aus der Zeit vor dem Rechnungstool als auch jede spätere Fassung, die noch von Hand in Excel gepflegt wurde. Ausgegeben wird nicht mehr nach Excel: Der Bericht fürs Amt ist ein PDF (siehe `bericht_pdf.py`). """ import re import logging from datetime import datetime, date import openpyxl from modell import Buchung, MONATE, STANDARD_SATZ, runde, zerlege_nummer log = logging.getLogger("bst.xlsx") ERSTE_ZEILE = 5 RE_BLATT_JAHR = re.compile(r"(20\d{2})") def _blatt_jahr(ws): """Jahr aus der Blatt-Überschrift ('JAN 2026 - Beherbergungssteuer') oder None.""" m = RE_BLATT_JAHR.search(str(ws["A1"].value or "")) return int(m.group(1)) if m else None def importiere(pfad: str, standard_satz: float = STANDARD_SATZ, quelle: str = "xlsx"): """ Liest eine Steuertabelle im Layout der Vorlage ein: A Datum | B Rechnungsnummer | C Nachname | D Übernachtungen | E Gezahlt | F Satz Zeilen, deren Datums-Jahr nicht zum Jahr des Monatsblattes passt (in der alten Tabelle steht z.B. 27.01.2025 mitten im Januar-2026-Blatt), werden als Vertipper behandelt und aufs Blatt-Jahr gezogen – mit Eintrag im Log. Gibt (buchungen, fehler) zurück; fehler = Liste von (Ort, Grund). """ buchungen, fehler = [], [] wb = openpyxl.load_workbook(pfad, data_only=True) for ws in wb.worksheets: if ws.title not in MONATE: # nur die Monatsblätter continue jahr_blatt = _blatt_jahr(ws) for row in ws.iter_rows(min_row=ERSTE_ZEILE, max_row=ws.max_row, max_col=6): datum, nummer, nachname, naechte, gezahlt, satz = [c.value for c in row] if datum is None and nachname is None: continue ort = f"{ws.title}!{row[0].row}" if not isinstance(datum, (datetime, date)): fehler.append((ort, f"Datum fehlt oder ist kein Datum: {datum!r}")) continue d = datum.date() if isinstance(datum, datetime) else datum if jahr_blatt and d.year != jahr_blatt: log.warning("%s: Datum %s passt nicht zum Blatt-Jahr %d – auf %d korrigiert", ort, d.strftime("%d.%m.%Y"), jahr_blatt, jahr_blatt) d = d.replace(year=jahr_blatt) jahr, nr = zerlege_nummer(nummer, d.year) if not nr: fehler.append((ort, "keine Rechnungsnummer")) continue buchungen.append(Buchung( datum=d, rechnungsnummer=nr, jahr=jahr, nachname=str(nachname or "").strip(), naechte=int(naechte or 0), gezahlt=runde(gezahlt or 0), satz=float(satz or standard_satz), quelle=quelle, pdf_pfad="", )) log.info("Import %s: %d Zeilen, %d übersprungen", pfad, len(buchungen), len(fehler)) return buchungen, fehler