beide Datensätze verbunden über die neu erstellte Spalte Tätigkeitsschlüssel und Ausgaben in Deutsch
This commit is contained in:
1 parent
e5bb23a666
commit
afa6ef61d4
2 files changed
+41
-19
No files matched your search
Binary file not shown.
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user