diff --git a/.github/copilot-tasks.md b/.github/copilot-tasks.md index b44be84..468fd6b 100644 --- a/.github/copilot-tasks.md +++ b/.github/copilot-tasks.md @@ -81,10 +81,10 @@ Projekt vollstaendig umsetzen: aus PS_PW.xlsx und Stellenbeschreibungen.xlsx mon - [x] Parameter `--month` und `--year` - [x] Pipeline orchestrieren: parse -> match -> validate -> preview -> write - [x] Kontrollierter Exit-Code bei Fehlern -- [ ] T14: Vorschau + Bestaetigung vor Schreiben - - [ ] Uebersicht zu Berichtsmonat, aktiven Faellen, Fehlern - - [ ] Liste der geplanten Ausgabedateien - - [ ] Interaktive Bestaetigung (Abbruch ohne Schreibzugriff moeglich) +- [x] T14: Vorschau + Bestaetigung vor Schreiben + - [x] Uebersicht zu Berichtsmonat, aktiven Faellen, Fehlern + - [x] Liste der geplanten Ausgabedateien + - [x] Interaktive Bestaetigung (Abbruch ohne Schreibzugriff moeglich) ## Phase 8 - Tests und Abnahme - [ ] T15: Smoke-Test fuer End-to-End-Lauf @@ -111,4 +111,4 @@ Projekt vollstaendig umsetzen: aus PS_PW.xlsx und Stellenbeschreibungen.xlsx mon 8. T15 -> T16 -> T17 ## Aktueller Startpunkt -- [ ] Naechste aktive Aufgabe: T14 (Vorschau + Bestaetigung vor Schreiben) +- [ ] Naechste aktive Aufgabe: T15 (Smoke-Test fuer End-to-End-Lauf) diff --git a/generate.py b/generate.py index 0405bc4..26e30a7 100644 --- a/generate.py +++ b/generate.py @@ -10,7 +10,11 @@ from src.taetigkeitsnachweis.logging_utils import configure_logging from src.taetigkeitsnachweis.matcher import match_assignments from src.taetigkeitsnachweis.parser import parse_allocation_records, parse_masterdata_records from src.taetigkeitsnachweis.validator import validate_business_rules, validate_sources -from src.taetigkeitsnachweis.writer import build_employee_workbooks, save_employee_workbooks +from src.taetigkeitsnachweis.writer import ( + build_employee_workbooks, + build_output_filename, + save_employee_workbooks, +) def _load_config(project_root: Path) -> dict: @@ -27,7 +31,7 @@ def _print_issues(issues: list, title: str) -> None: click.echo(f"- [{issue.severity}] {issue.code}: {issue.message}") -def _print_preview(month: int, year: int, matched_assignments: list) -> None: +def _print_preview(month: int, year: int, matched_assignments: list, planned_files: list[str]) -> None: click.echo("=== Vorschau der ermittelten Werte ===") click.echo(f"Berichtsmonat : {month:02d}/{year}") click.echo(f"Aktive Faelle : {len(matched_assignments)}") @@ -40,11 +44,16 @@ def _print_preview(month: int, year: int, matched_assignments: list) -> None: f"{item.foerderquote_percent:.2f}%" ) + click.echo("--- Geplante Ausgabedateien ---") + for file_name in planned_files: + click.echo(f"- {file_name}") + @click.command() @click.option("--month", type=click.IntRange(1, 12), required=True) @click.option("--year", type=click.IntRange(2000, 2100), required=True) -def main(month: int, year: int) -> None: +@click.option("--yes", is_flag=True, default=False, help="Skip interactive confirmation.") +def main(month: int, year: int, yes: bool) -> None: configure_logging() project_root = Path(__file__).resolve().parent @@ -93,7 +102,30 @@ def main(month: int, year: int) -> None: if any(issue.severity == "error" for issue in all_issues): raise click.ClickException("Validation failed after matching.") - _print_preview(month=month, year=year, matched_assignments=matched_assignments) + filename_pattern = config["output"]["filename_pattern"] + unique_employees = sorted({item.allocation.employee_name for item in matched_assignments}) + planned_files = [ + build_output_filename( + filename_pattern=filename_pattern, + employee_name=employee_name, + report_year=year, + report_month=month, + ) + for employee_name in unique_employees + ] + + _print_preview( + month=month, + year=year, + matched_assignments=matched_assignments, + planned_files=planned_files, + ) + + if not yes: + confirmed = click.confirm("Dateien jetzt schreiben?", default=False) + if not confirmed: + click.echo("Abbruch ohne Schreibzugriff.") + return employee_workbooks = build_employee_workbooks( config=config,