312 lines
13 KiB
Python
312 lines
13 KiB
Python
from __future__ import annotations
|
|
|
|
from collections import OrderedDict
|
|
from datetime import date, datetime
|
|
|
|
from django.utils import formats
|
|
from django.utils.translation import override
|
|
|
|
from .form_builder import (
|
|
LOCKED_FIELD_RULES,
|
|
LOCKED_SECTION_RULES,
|
|
get_custom_field_configs,
|
|
ensure_form_field_configs,
|
|
ensure_form_section_configs,
|
|
get_default_page_map,
|
|
get_section_labels,
|
|
get_section_order,
|
|
)
|
|
from .forms import OffboardingRequestForm, OnboardingRequestForm
|
|
from .models import FormCustomFieldConfig
|
|
|
|
PDF_SECTION_TITLES = {
|
|
"onboarding": {
|
|
"stammdaten": "Stammdaten",
|
|
"vertrag": "Vertrag",
|
|
"itsetup": "IT-Setup",
|
|
"abschluss": "Abschluss",
|
|
},
|
|
"offboarding": {
|
|
"mitarbeitende": "Mitarbeitende",
|
|
"austritt": "Austritt",
|
|
"abschluss": "Abschluss",
|
|
},
|
|
}
|
|
|
|
PDF_FIELD_LABELS = {
|
|
"onboarding": {
|
|
"full_name": {"de": "Name", "en": "Name"},
|
|
},
|
|
"offboarding": {
|
|
"full_name": {"de": "Name", "en": "Name"},
|
|
},
|
|
}
|
|
|
|
PDF_EXCLUDED_FIELDS = {
|
|
"onboarding": {
|
|
"first_name",
|
|
"last_name",
|
|
"onboarded_by_email",
|
|
"agreement_confirm",
|
|
"signature_url",
|
|
"signature_image",
|
|
},
|
|
"offboarding": set(),
|
|
}
|
|
|
|
PDF_BOOLEAN_CONTROL_FIELDS = {
|
|
"onboarding": {
|
|
"order_business_cards",
|
|
"group_mailboxes_required_choice",
|
|
"additional_hardware_needed_choice",
|
|
"additional_software_needed_choice",
|
|
"additional_access_needed_choice",
|
|
"successor_required_choice",
|
|
"inherit_phone_number_choice",
|
|
},
|
|
"offboarding": set(),
|
|
}
|
|
|
|
|
|
def _normalized_lang(language_code: str | None) -> str:
|
|
return (language_code or "de").split("-")[0].lower() or "de"
|
|
|
|
|
|
def _yes_no_text(language_code: str | None) -> tuple[str, str]:
|
|
lang = _normalized_lang(language_code)
|
|
if lang == "en":
|
|
return "Yes", "No"
|
|
return "Ja", "Nein"
|
|
|
|
|
|
def _not_available_text(language_code: str | None) -> str:
|
|
return "Not provided" if _normalized_lang(language_code) == "en" else "Keine Angabe"
|
|
|
|
|
|
def _split_name(full_name: str) -> tuple[str, str]:
|
|
parts = (full_name or "").split()
|
|
if not parts:
|
|
return "", ""
|
|
return parts[0], " ".join(parts[1:])
|
|
|
|
|
|
def _split_multiline(value: str) -> list[str]:
|
|
return [line.strip() for line in (value or "").splitlines() if line.strip()]
|
|
|
|
|
|
def _format_date(value, language_code: str | None) -> str:
|
|
if not value:
|
|
return ""
|
|
if isinstance(value, datetime):
|
|
with override(_normalized_lang(language_code)):
|
|
return formats.date_format(value, "DATETIME_FORMAT", use_l10n=True)
|
|
if isinstance(value, date):
|
|
with override(_normalized_lang(language_code)):
|
|
return formats.date_format(value, "DATE_FORMAT", use_l10n=True)
|
|
return str(value)
|
|
|
|
|
|
def _coerce_text(value) -> str:
|
|
if value is None:
|
|
return ""
|
|
if isinstance(value, (date, datetime)):
|
|
return str(value)
|
|
return str(value).strip()
|
|
|
|
|
|
def _field_value_from_request(form_type: str, request_obj, field_name: str, language_code: str | None):
|
|
yes_text, no_text = _yes_no_text(language_code)
|
|
first_name, last_name = _split_name(getattr(request_obj, "full_name", ""))
|
|
|
|
onboarding_map = {
|
|
"first_name": first_name,
|
|
"last_name": last_name,
|
|
"full_name": getattr(request_obj, "full_name", ""),
|
|
"gender": getattr(request_obj, "get_gender_display", lambda: "")() or getattr(request_obj, "gender", ""),
|
|
"job_title": getattr(request_obj, "job_title", ""),
|
|
"department": getattr(request_obj, "department", ""),
|
|
"work_email": getattr(request_obj, "work_email", ""),
|
|
"order_business_cards": yes_text if getattr(request_obj, "order_business_cards", False) else no_text,
|
|
"business_card_name": getattr(request_obj, "business_card_name", ""),
|
|
"business_card_title": getattr(request_obj, "business_card_title", ""),
|
|
"business_card_email": getattr(request_obj, "business_card_email", ""),
|
|
"business_card_phone": getattr(request_obj, "business_card_phone", ""),
|
|
"contract_start": _format_date(getattr(request_obj, "contract_start", None), language_code),
|
|
"employment_type": getattr(request_obj, "get_employment_type_display", lambda: "")() or getattr(request_obj, "employment_type", ""),
|
|
"employment_end_date": _format_date(getattr(request_obj, "employment_end_date", None), language_code),
|
|
"handover_date": _format_date(getattr(request_obj, "handover_date", None), language_code),
|
|
"group_mailboxes_required_choice": yes_text if getattr(request_obj, "group_mailboxes_required", False) else no_text,
|
|
"group_mailboxes": _split_multiline(getattr(request_obj, "group_mailboxes", "")),
|
|
"needed_devices_multi": _split_multiline(getattr(request_obj, "needed_devices", "")),
|
|
"additional_hardware_needed_choice": yes_text if getattr(request_obj, "additional_hardware_needed", False) else no_text,
|
|
"additional_hardware_multi": _split_multiline(getattr(request_obj, "additional_hardware", "")),
|
|
"additional_hardware_other": getattr(request_obj, "additional_hardware_other", ""),
|
|
"needed_software_multi": _split_multiline(getattr(request_obj, "needed_software", "")),
|
|
"additional_software_needed_choice": yes_text if getattr(request_obj, "additional_software_needed", False) else no_text,
|
|
"additional_software_multi": _split_multiline(getattr(request_obj, "additional_software", "")),
|
|
"additional_software": getattr(request_obj, "additional_software", ""),
|
|
"needed_accesses_multi": _split_multiline(getattr(request_obj, "needed_accesses", "")),
|
|
"additional_access_needed_choice": yes_text if getattr(request_obj, "additional_access_needed", False) else no_text,
|
|
"additional_access_text": _split_multiline(getattr(request_obj, "additional_access_text", "")),
|
|
"needed_workspace_groups_multi": _split_multiline(getattr(request_obj, "needed_workspace_groups", "")),
|
|
"needed_resources_multi": _split_multiline(getattr(request_obj, "needed_resources", "")),
|
|
"successor_required_choice": yes_text if getattr(request_obj, "successor_required", False) else no_text,
|
|
"successor_name": getattr(request_obj, "successor_name", ""),
|
|
"inherit_phone_number_choice": yes_text if getattr(request_obj, "inherit_phone_number", False) else no_text,
|
|
"phone_number_choice": getattr(request_obj, "phone_number", ""),
|
|
"additional_notes": getattr(request_obj, "additional_notes", ""),
|
|
"signature_url": getattr(request_obj, "signature_url", ""),
|
|
"signature_image": getattr(getattr(request_obj, "signature_image", None), "name", "") or "",
|
|
"onboarded_by_email": getattr(request_obj, "onboarded_by_email", ""),
|
|
"agreement_confirm": yes_text if _coerce_text(getattr(request_obj, "agreement", "")) else no_text,
|
|
}
|
|
offboarding_map = {
|
|
"full_name": getattr(request_obj, "full_name", ""),
|
|
"work_email": getattr(request_obj, "work_email", ""),
|
|
"department": getattr(request_obj, "department", ""),
|
|
"job_title": getattr(request_obj, "job_title", ""),
|
|
"last_working_day": _format_date(getattr(request_obj, "last_working_day", None), language_code),
|
|
"notes": getattr(request_obj, "notes", ""),
|
|
}
|
|
value_map = onboarding_map if form_type == "onboarding" else offboarding_map
|
|
return value_map.get(field_name, "")
|
|
|
|
|
|
def _field_kind(value) -> str:
|
|
if isinstance(value, list):
|
|
return "list"
|
|
return "text"
|
|
|
|
|
|
def _is_empty_value(value) -> bool:
|
|
if isinstance(value, list):
|
|
return len(value) == 0
|
|
return _coerce_text(value) == ""
|
|
|
|
|
|
def _field_meta(form_type: str, field_name: str, language_code: str | None) -> tuple[str, str]:
|
|
custom_label = PDF_FIELD_LABELS.get(form_type, {}).get(field_name, {})
|
|
if custom_label:
|
|
return custom_label.get(_normalized_lang(language_code), field_name), ""
|
|
form_class = OnboardingRequestForm if form_type == "onboarding" else OffboardingRequestForm
|
|
base_field = form_class.base_fields.get(field_name)
|
|
if not base_field:
|
|
return field_name, ""
|
|
with override(_normalized_lang(language_code)):
|
|
label = str(base_field.label or field_name)
|
|
help_text = str(base_field.help_text or "").strip()
|
|
return label, help_text
|
|
|
|
|
|
def build_pdf_sections(form_type: str, request_obj, language_code: str | None = None) -> list[dict]:
|
|
language_code = _normalized_lang(language_code or getattr(request_obj, "preferred_language", None))
|
|
default_page_map = get_default_page_map(form_type)
|
|
section_order = get_section_order(form_type)
|
|
section_labels = get_section_labels(form_type)
|
|
field_names = list(default_page_map.keys())
|
|
configs = ensure_form_field_configs(form_type, field_names)
|
|
section_configs = ensure_form_section_configs(form_type)
|
|
locked_fields = LOCKED_FIELD_RULES.get(form_type, set())
|
|
locked_sections = LOCKED_SECTION_RULES.get(form_type, set())
|
|
|
|
sections: OrderedDict[str, dict] = OrderedDict()
|
|
for key in section_order:
|
|
section_cfg = section_configs.get(key)
|
|
is_visible = True
|
|
if key not in locked_sections and section_cfg is not None:
|
|
is_visible = bool(section_cfg.is_visible)
|
|
if not is_visible:
|
|
continue
|
|
sections[key] = {
|
|
"key": key,
|
|
"title": PDF_SECTION_TITLES.get(form_type, {}).get(key, section_labels.get(key, key)),
|
|
"fields": [],
|
|
}
|
|
|
|
ordered_configs = sorted(
|
|
configs.values(),
|
|
key=lambda cfg: (cfg.sort_order, cfg.field_name),
|
|
)
|
|
for cfg in ordered_configs:
|
|
field_name = cfg.field_name
|
|
section_key = cfg.page_key or default_page_map.get(field_name, "")
|
|
if section_key not in sections:
|
|
continue
|
|
if field_name in PDF_EXCLUDED_FIELDS.get(form_type, set()):
|
|
continue
|
|
if field_name not in locked_fields and not cfg.is_visible:
|
|
continue
|
|
|
|
base_label, base_help_text = _field_meta(form_type, field_name, language_code)
|
|
label = cfg.translated_label_override(language_code) or base_label
|
|
help_text = cfg.translated_help_text_override(language_code) or base_help_text
|
|
raw_value = _field_value_from_request(form_type, request_obj, field_name, language_code)
|
|
sections[section_key]["fields"].append(
|
|
{
|
|
"name": field_name,
|
|
"label": label,
|
|
"help_text": help_text,
|
|
"kind": _field_kind(raw_value),
|
|
"value": raw_value,
|
|
"is_empty": _is_empty_value(raw_value),
|
|
"is_locked": field_name in locked_fields,
|
|
}
|
|
)
|
|
|
|
custom_values = getattr(request_obj, 'custom_field_values', {}) or {}
|
|
for cfg in get_custom_field_configs(form_type):
|
|
if cfg.section_key not in sections:
|
|
continue
|
|
raw_value = custom_values.get(cfg.field_key)
|
|
if cfg.field_type == FormCustomFieldConfig.FIELD_TYPE_CHECKBOX:
|
|
raw_value = _yes_no_text(language_code)[0] if raw_value else ''
|
|
sections[cfg.section_key]["fields"].append(
|
|
{
|
|
"name": f"custom__{cfg.field_key}",
|
|
"label": cfg.translated_label(language_code),
|
|
"help_text": cfg.translated_help_text(language_code),
|
|
"kind": _field_kind(raw_value),
|
|
"value": raw_value,
|
|
"is_empty": _is_empty_value(raw_value),
|
|
"is_locked": False,
|
|
}
|
|
)
|
|
|
|
not_available = _not_available_text(language_code)
|
|
result = []
|
|
for section in sections.values():
|
|
visible_fields = []
|
|
for field in section["fields"]:
|
|
if (
|
|
field["name"] in PDF_BOOLEAN_CONTROL_FIELDS.get(form_type, set())
|
|
and _coerce_text(field["value"]).lower() in {"nein", "no"}
|
|
):
|
|
continue
|
|
display_value = field["value"] if field["kind"] == "list" else (_coerce_text(field["value"]) or not_available)
|
|
visible_fields.append(
|
|
{
|
|
**field,
|
|
"display_value": display_value,
|
|
}
|
|
)
|
|
render_fields = [field for field in visible_fields if not field["is_empty"]]
|
|
scalar_fields = [field for field in render_fields if field["kind"] != "list"]
|
|
list_fields = [field for field in render_fields if field["kind"] == "list"]
|
|
scalar_rows = [scalar_fields[index:index + 2] for index in range(0, len(scalar_fields), 2)]
|
|
for row in scalar_rows:
|
|
if len(row) < 2:
|
|
row.append(None)
|
|
result.append(
|
|
{
|
|
"key": section["key"],
|
|
"title": section["title"],
|
|
"fields": visible_fields,
|
|
"render_fields": render_fields,
|
|
"scalar_fields": scalar_fields,
|
|
"list_fields": list_fields,
|
|
"scalar_rows": scalar_rows,
|
|
"has_content": bool(render_fields),
|
|
}
|
|
)
|
|
return result
|