diff --git a/config.json b/config.json index 368a471..a1a975b 100644 --- a/config.json +++ b/config.json @@ -41,9 +41,10 @@ "antragsnummer": "B", "name": "C", "qualifikation": "D", - "stellenanteil": "E", - "funktion": "F", - "taetigkeit": "G" + "project_scope": "E", + "stellenanteil": "F", + "funktion": "G", + "taetigkeit": "H" }, "decimal": { "decimal_separator": ",", diff --git a/src/taetigkeitsnachweis/matcher.py b/src/taetigkeitsnachweis/matcher.py index a73b77c..9748b92 100644 --- a/src/taetigkeitsnachweis/matcher.py +++ b/src/taetigkeitsnachweis/matcher.py @@ -46,12 +46,23 @@ def _name_keys(name: str) -> set[str]: def _disambiguate_by_project_key( - candidates: list, + candidates: list[MasterDataRecord], project_key: str, -) -> object: - """Return the single candidate whose Funktion contains the project key, or None.""" - key = project_key.strip().casefold() - matched = [c for c in candidates if key in c.funktion.casefold()] +) -> MasterDataRecord | None: + """Return the single candidate matching allocation project key via masterdata project_scope.""" + parts = [ + token.strip().casefold() + for token in re.split(r"[/,;|]", project_key) + if token.strip() + ] + project_tokens = set(parts or [project_key.strip().casefold()]) + + matched = [] + for candidate in candidates: + candidate_scope = (candidate.project_scope or "PS").strip().casefold() + if candidate_scope in project_tokens: + matched.append(candidate) + return matched[0] if len(matched) == 1 else None @@ -105,7 +116,7 @@ def match_assignments( if len(candidates) == 1: selected = candidates[0] else: - # Multiple candidates: disambiguate by project key in Funktion + # Multiple candidates: disambiguate by explicit project scope column. selected = _disambiguate_by_project_key(candidates, allocation.project_key) if selected is None: issues.append( @@ -114,8 +125,8 @@ def match_assignments( message=( f"Ambiguous master-data for '{allocation.employee_name}' / " f"'{allocation.project_key}': {len(candidates)} rows, " - f"none or multiple match Funktion. " - f"Funktionen: {[c.funktion for c in candidates]}" + f"none or multiple match project_scope. " + f"project_scope values: {[c.project_scope for c in candidates]}" ), severity="error", context=allocation.employee_name, diff --git a/src/taetigkeitsnachweis/models.py b/src/taetigkeitsnachweis/models.py index 38ee4cb..e0134b8 100644 --- a/src/taetigkeitsnachweis/models.py +++ b/src/taetigkeitsnachweis/models.py @@ -19,6 +19,7 @@ class MasterDataRecord: antragsnummer: str name: str qualifikation: str + project_scope: str stellenanteil_percent: float funktion: str taetigkeit: str diff --git a/src/taetigkeitsnachweis/parser.py b/src/taetigkeitsnachweis/parser.py index 5d81a5f..c59fb5d 100644 --- a/src/taetigkeitsnachweis/parser.py +++ b/src/taetigkeitsnachweis/parser.py @@ -243,6 +243,7 @@ def parse_masterdata_records( antragsnummer_col = column_index_from_string(columns_cfg["antragsnummer"]) name_col = column_index_from_string(columns_cfg["name"]) qualifikation_col = column_index_from_string(columns_cfg["qualifikation"]) + project_scope_col = column_index_from_string(columns_cfg["project_scope"]) stellenanteil_col = column_index_from_string(columns_cfg["stellenanteil"]) funktion_col = column_index_from_string(columns_cfg["funktion"]) taetigkeit_col = column_index_from_string(columns_cfg["taetigkeit"]) @@ -254,12 +255,16 @@ def parse_masterdata_records( if not name: continue - stellenanteil = _parse_german_decimal( - worksheet.cell(row=row_index, column=stellenanteil_col).value, - field_name=f"stellenanteil row {row_index}", - decimal_separator=decimal_cfg.get("decimal_separator", ","), - thousands_separator=decimal_cfg.get("thousands_separator", "."), + stellenanteil_raw = worksheet.cell(row=row_index, column=stellenanteil_col).value + stellenanteil = _to_float_or_none(stellenanteil_raw) + if stellenanteil is None: + stellenanteil = 0.0 + + project_scope = _to_text( + worksheet.cell(row=row_index, column=project_scope_col).value ) + if not project_scope: + project_scope = "PS" records.append( MasterDataRecord( @@ -271,6 +276,7 @@ def parse_masterdata_records( qualifikation=_to_text( worksheet.cell(row=row_index, column=qualifikation_col).value ), + project_scope=project_scope, stellenanteil_percent=stellenanteil, funktion=_to_text(worksheet.cell(row=row_index, column=funktion_col).value), taetigkeit=_to_text( diff --git a/src/taetigkeitsnachweis/validator.py b/src/taetigkeitsnachweis/validator.py index a06be1b..f02a1a0 100644 --- a/src/taetigkeitsnachweis/validator.py +++ b/src/taetigkeitsnachweis/validator.py @@ -132,6 +132,7 @@ def validate_sources( master_cols["antragsnummer"], master_cols["name"], master_cols["qualifikation"], + master_cols["project_scope"], master_cols["stellenanteil"], master_cols["funktion"], master_cols["taetigkeit"],