beide Datensätze verbunden über die neu erstellte Spalte Tätigkeitsschlüssel und Ausgaben in Deutsch

This commit is contained in:
trnq1ll0 committed 2026-05-27 18:48:04 +02:00
1 parent e5bb23a666
commit afa6ef61d4
2 files changed
+41 -19

No files matched your search

Binary file not shown.
+41 -19
View File
@@ -48,22 +48,28 @@ def _name_keys(name: str) -> set[str]:
def _disambiguate_by_project_key(
candidates: list[MasterDataRecord],
project_key: str,
) -> 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()])
) -> tuple[MasterDataRecord | None, str]:
"""Return the single candidate matching allocation project key via masterdata taetigkeit.
Returns a tuple of (matched_record, status) where status is one of:
- "ok": exactly one match found
- "no_match": no candidate has a matching taetigkeit
- "multiple": more than one candidate matches
"""
normalized_key = project_key.strip().casefold()
matched = []
for candidate in candidates:
candidate_scope = (candidate.project_scope or "PS").strip().casefold()
if candidate_scope in project_tokens:
candidate_taetigkeit = (candidate.taetigkeit or "").strip().casefold()
if candidate_taetigkeit == normalized_key:
matched.append(candidate)
return matched[0] if len(matched) == 1 else None
if len(matched) == 1:
return matched[0], "ok"
elif len(matched) == 0:
return None, "no_match"
else:
return None, "multiple"
def match_assignments(
@@ -103,10 +109,11 @@ def match_assignments(
ValidationIssue(
code="MISSING_MASTERDATA",
message=(
f"No master-data match for employee '{allocation.employee_name}' "
f"(project '{allocation.project_key}')."
f"In der Stellenbeschreibung ist kein Datensatz für "
f"'{allocation.employee_name}' hinterlegt "
f"(Projekt '{allocation.project_key}'). Keine Förderung möglich."
),
severity="error",
severity="info",
context=allocation.employee_name,
)
)
@@ -116,17 +123,32 @@ def match_assignments(
if len(candidates) == 1:
selected = candidates[0]
else:
# Multiple candidates: disambiguate by explicit project scope column.
selected = _disambiguate_by_project_key(candidates, allocation.project_key)
if selected is None:
# Multiple candidates: disambiguate by Tätigkeitsschlüssel.
selected, match_status = _disambiguate_by_project_key(candidates, allocation.project_key)
if match_status == "no_match":
issues.append(
ValidationIssue(
code="NO_MATCHING_TAETIGKEIT",
message=(
f"No Tätigkeitsschlüssel '{allocation.project_key}' found in "
f"master-data for '{allocation.employee_name}'. "
f"Available: {[c.taetigkeit for c in candidates]}. "
f"Keine Förderung möglich."
),
severity="warning",
context=allocation.employee_name,
)
)
continue
elif selected is None:
issues.append(
ValidationIssue(
code="AMBIGUOUS_MASTERDATA",
message=(
f"Ambiguous master-data for '{allocation.employee_name}' / "
f"'{allocation.project_key}': {len(candidates)} rows, "
f"none or multiple match project_scope. "
f"project_scope values: {[c.project_scope for c in candidates]}"
f"multiple match taetigkeit. "
f"taetigkeit values: {[c.taetigkeit for c in candidates]}"
),
severity="error",
context=allocation.employee_name,