From 5f3a820641e77d40d30838759aeeafbf49a1e32c Mon Sep 17 00:00:00 2001 From: Rob Date: Tue, 21 Apr 2026 16:12:49 +0200 Subject: [PATCH] Configure template target cells and add skip-errors mode --- .github/copilot-tasks.md | 9 +++++++-- config.json | 18 +++++++++--------- generate.py | 8 ++++++-- src/taetigkeitsnachweis/validator.py | 7 ++++--- src/taetigkeitsnachweis/writer.py | 6 +++++- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/.github/copilot-tasks.md b/.github/copilot-tasks.md index a47c1f9..b04b664 100644 --- a/.github/copilot-tasks.md +++ b/.github/copilot-tasks.md @@ -89,9 +89,14 @@ Projekt vollstaendig umsetzen: aus PS_PW.xlsx und Stellenbeschreibungen.xlsx mon ## Phase 8 - Tests und Abnahme - [ ] T15: Smoke-Test fuer End-to-End-Lauf - [x] Beispielmonat ausfuehren + - [x] Pipeline laeuft bis Dateiausgabe durch (0 Dateien, da 0 gematchte Faelle) - [ ] Dateien in `output/` entstehen erwartungsgemaess - - [ ] Blocker: Masterdata-Matches fehlen fuer mehrere aktive Namen. - - [ ] Blocker: Template-Regel verletzt (aktuell 5 Sheets statt 1) und template_targets sind noch nicht konfiguriert. + - [ ] DATENBLOCKER: `Stellenbeschreibungen.xlsx` enthaelt nur Meyer,Johannes (25%/75%). + Alle anderen aktiven Mitarbeiter aus PS_PW.xlsx fehlen als Stammdaten. + Meyer's Foerderquote (85%/15%) stimmt nicht mit eingetragenem Stellenanteil (25%/75%) ueberein. + -> Stellenbeschreibungen.xlsx mit allen aktiven Mitarbeitern und korrekten Anteilen befuellen. + - [x] Template-Zielzellen konfiguriert (A3, A17, Q17, A21, Q21, A25, A31, I95) + - [x] Template-Sheet-Pfad konfiguriert (_) - [ ] T16: Fachtests definieren und ausfuehren - [ ] Mehrfachzuordnung eines Mitarbeiters - [ ] Fehlender Match diff --git a/config.json b/config.json index 77a9e2b..368a471 100644 --- a/config.json +++ b/config.json @@ -12,7 +12,7 @@ "sheets": { "allocation": "Tabelle1", "masterdata": "Tabelle1", - "template_source": "Tabelle1" + "template_source": "_" }, "allocation": { "header_month_marker_row": 2, @@ -66,14 +66,14 @@ "sheet_name_pattern": "{index}_{funktion}" }, "template_targets": { - "antragsnummer": "", - "name_vorname": "", - "qualifikation": "", - "funktion": "", - "taetigkeiten": "", - "reporting_month_label": "", - "foerderquote": "", - "generated_on": "" + "antragsnummer": "A3", + "name_vorname": "A17", + "qualifikation": "A21", + "funktion": "A25", + "taetigkeiten": "A31", + "reporting_month_label": "Q21", + "foerderquote": "Q17", + "generated_on": "I95" }, "runtime": { "abort_on_validation_errors": true, diff --git a/generate.py b/generate.py index 26e30a7..087bfe9 100644 --- a/generate.py +++ b/generate.py @@ -53,7 +53,8 @@ def _print_preview(month: int, year: int, matched_assignments: list, planned_fil @click.option("--month", type=click.IntRange(1, 12), required=True) @click.option("--year", type=click.IntRange(2000, 2100), required=True) @click.option("--yes", is_flag=True, default=False, help="Skip interactive confirmation.") -def main(month: int, year: int, yes: bool) -> None: +@click.option("--skip-errors", is_flag=True, default=False, help="Generate files for matched records, skip unresolved ones.") +def main(month: int, year: int, yes: bool, skip_errors: bool) -> None: configure_logging() project_root = Path(__file__).resolve().parent @@ -99,8 +100,11 @@ def main(month: int, year: int, yes: bool) -> None: _print_issues(business_issues, "=== Business validation ===") all_issues = [*match_issues, *business_issues] - if any(issue.severity == "error" for issue in all_issues): + has_errors = any(issue.severity == "error" for issue in all_issues) + if has_errors and not skip_errors: raise click.ClickException("Validation failed after matching.") + if has_errors and skip_errors: + click.echo("Warnhinweis: Fehler vorhanden. Weiterfahrt mit --skip-errors (nur gematchte Faelle werden erzeugt).") filename_pattern = config["output"]["filename_pattern"] unique_employees = sorted({item.allocation.employee_name for item in matched_assignments}) diff --git a/src/taetigkeitsnachweis/validator.py b/src/taetigkeitsnachweis/validator.py index 75cb6d8..a06be1b 100644 --- a/src/taetigkeitsnachweis/validator.py +++ b/src/taetigkeitsnachweis/validator.py @@ -319,12 +319,13 @@ def validate_business_rules( if template_path.exists(): template_wb = load_workbook(template_path, data_only=True) - if len(template_wb.sheetnames) != 1: + configured_sheet = config["sheets"].get("template_source", "") + if configured_sheet not in template_wb.sheetnames: issues.append( ValidationIssue( - code="TEMPLATE_SHEET_COUNT", + code="TEMPLATE_SHEET_MISSING", message=( - f"Template workbook must contain exactly one sheet, found {len(template_wb.sheetnames)}." + f"Configured template sheet '{configured_sheet}' not found in template workbook." ), severity="error", ) diff --git a/src/taetigkeitsnachweis/writer.py b/src/taetigkeitsnachweis/writer.py index ebcd780..10ee3af 100644 --- a/src/taetigkeitsnachweis/writer.py +++ b/src/taetigkeitsnachweis/writer.py @@ -67,7 +67,11 @@ def build_employee_workbooks( for employee_name, assignments in assignments_by_employee.items(): workbook = load_workbook(template_path) - base_sheet = workbook.worksheets[0] + template_sheet_name = config["sheets"].get("template_source", "") + if template_sheet_name and template_sheet_name in workbook.sheetnames: + base_sheet = workbook[template_sheet_name] + else: + base_sheet = workbook.worksheets[0] for index, assignment in enumerate(assignments, start=1): if index == 1: