diff --git a/src/wce_crm/custom_widget_registry.py b/src/wce_crm/custom_widget_registry.py new file mode 100644 index 0000000..f1c6d8a --- /dev/null +++ b/src/wce_crm/custom_widget_registry.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +from typing import Final + +CUSTOM_WIDGET_NAMES: Final[frozenset] = frozenset( + [ + "grunderfassung_suche", + ] +) diff --git a/src/wce_crm/data_models.py b/src/wce_crm/data_models.py new file mode 100644 index 0000000..9575ea7 --- /dev/null +++ b/src/wce_crm/data_models.py @@ -0,0 +1,297 @@ +from __future__ import annotations + +import datetime +import json +from typing import Annotated, Any, Final + +from pydantic import ( + AwareDatetime, + BaseModel, + ConfigDict, + EmailStr, + Field, + field_validator, + model_validator, +) + +ValidAge = Annotated[int, Field(ge=0, le=99)] + + +def _parse_json(value: Any) -> str: + if isinstance(value, datetime.date): + return value.isoformat() + elif isinstance(value, datetime.datetime): + return value.isoformat() + else: + raise TypeError + + +COLUMN_SEP: Final[str] = "__" + + +class FlatBaseModel(BaseModel): + """ + Optimised Pydantic base class, which parses JSON strings and column + separators recursively and correctly + """ + + @classmethod + def _recursive_parse_json( + cls, + data: Any, + ) -> Any: + """look for JSON list strings and parse them""" + if isinstance(data, str) and data.startswith("[") and data.endswith("]"): + try: + parsed = json.loads(data) + # Falls die Liste selbst wieder konvertiert werden muss (z.B. Sub-Dicts) + return cls._recursive_parse_json(parsed) + except json.JSONDecodeError: + return data + elif isinstance(data, dict): + return {k: cls._recursive_parse_json(v) for k, v in data.items()} + elif isinstance(data, list): + return [cls._recursive_parse_json(item) for item in data] + return data + + @classmethod + def _recursive_unflatten( + cls, + data: Any, + ) -> Any: + """building nested structure using column spearator sequence""" + if isinstance(data, dict): + unflattened_level = {} + for key, value in data.items(): + if COLUMN_SEP in key: + parts = key.split(COLUMN_SEP) + aktuell = unflattened_level + for part in parts[:-1]: + if part not in aktuell or not isinstance(aktuell[part], dict): + aktuell[part] = {} + aktuell = aktuell[part] + aktuell[parts[-1]] = value + else: + unflattened_level[key] = value + + return {k: cls._recursive_unflatten(v) for k, v in unflattened_level.items()} + + elif isinstance(data, list): + return [cls._recursive_unflatten(item) for item in data] + + return data + + @model_validator(mode="before") + @classmethod + def __unflatten_input( + cls, + data: Any, + ) -> Any: # type: ignore + """entry control: prepare flat DB/GUI data for Pydantic""" + if not isinstance(data, dict): + return data + + # setp 1: convert all JSON-Strings to lists + json_parsed_data = cls._recursive_parse_json(data) + # step 2: build nested structure based on defined separator sequence + final_nested_data = cls._recursive_unflatten(json_parsed_data) + + return final_nested_data + + def to_db(self, *args, **kwargs) -> dict[str, Any]: + """output for DB: flat, lists as JSON-Strings""" + nested = super().model_dump(*args, **kwargs) + return self.__flatten_dict(nested, serialize_lists=True) + + def to_gui(self, *args, **kwargs) -> dict[str, Any]: + """output for GUI: flat, but lists remain Python lists""" + nested = super().model_dump(*args, **kwargs) + return self.__flatten_dict(nested, serialize_lists=False) + + @classmethod + def __flatten_dict( + cls, + nested_dict: dict, + parent_key: str = "", + serialize_lists: bool = True, + ) -> dict[str, Any]: + """recursive function to flatten the structure (for outputs)""" + items = [] + for k, v in nested_dict.items(): + new_key = f"{parent_key}{COLUMN_SEP}{k}" if parent_key else k + + if isinstance(v, dict): + items.extend(cls.__flatten_dict(v, new_key, serialize_lists).items()) + elif isinstance(v, list): + processed_list = [] + for item in v: + if isinstance(item, dict): + processed_list.append( + cls.__flatten_dict(item, serialize_lists=serialize_lists) + ) + else: + processed_list.append(item) + + if serialize_lists: + items.append((new_key, json.dumps(processed_list, default=_parse_json))) + else: + items.append((new_key, processed_list)) + else: + items.append((new_key, v)) + return dict(items) + + +class Grunderfassung(FlatBaseModel): + # default in SQLAlchemy with lambda and timezone-aware datetime + Metadaten_erstellung: AwareDatetime | None = None + Metadaten_aktualisierung: AwareDatetime | None = None # see above + Metadaten_nutzer: str | None + Metadaten_wiedereintrittsdatum: datetime.date | None = None + Grunderfassung_fallnummer: str + Grunderfassung_notiz: str | None + + Partnersuche: Grunderfassung_PartnerSuche | None = None + Projektrelevanz: Grunderfassung_Projektrelevanz + Kontaktperson: Grunderfassung_Kontaktperson + Stammdaten: Grunderfassung_Stammdaten + WeitereInfos: Grunderfassung_WeitereInfos + Schulbildung: list[Grunderfassung_Schulbildung] + HoehereBildung: list[Grunderfassung_HoehereBildung] + Arbeitserfahrung: list[Grunderfassung_Arbeitserfahrung] + Sprachkenntnisse: list[Grunderfassung_Sprachen] + + +class Grunderfassung_PartnerSuche(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + un_suche: int | None + person_suche: int | None + kanal_aufmerksamkeit: str | None + + +class Grunderfassung_Projektrelevanz(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + relevanz: str + foerderperiode: str | None = None + + +class Grunderfassung_Kontaktperson(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + KP_name_partner: str | None + KP_titel: str | None + KP_anrede_anschrift: str | None + KP_name: str | None + KP_vorname: str | None + KP_festnetznummer: str | None + KP_mobilfunknummer: str | None + KP_email: EmailStr | None + KP_funktion_beziehung: str | None + KP_adresse: str | None + + +class Grunderfassung_Stammdaten(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + titel: str | None + anrede_anschrift: str + name: str + vorname: str | None + geburtsdatum: datetime.date | None + herkunftsland: str + staatsangehoerigkeit: str | None + rueckkehrer: bool | None + aufenthaltsort: str | None + strasse: str | None + hausnummer: str | None + PLZ: str | None + ort: str | None + bundesland: str | None + land: str | None + festnetznummer: str | None + mobilfunknummer: str | None + email: EmailStr | None + familienstand: str | None + anzahl_kinder: Grunderfassung_Stammdaten_AnzahlKinder + + @field_validator("rueckkehrer", mode="before") + @classmethod + def str_to_bool(cls, value: Any) -> Any: + if isinstance(value, str): + value = value.strip().lower() + + if value == "ja": + return True + elif value == "nein": + return False + + raise ValueError("Wert muss 'ja', 'nein', True oder False sein.") + + return value + + +class Grunderfassung_Stammdaten_AnzahlKinder(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + anzahl: int | None + alter: list[ValidAge | None] | None = None + + +class Grunderfassung_WeitereInfos(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + WI_deutsch_sprache: str | None + WI_aufenthaltstitel: str | None + WI_gueltigkeit_aufenthaltstitel: datetime.date | None + WI_arbeitsstatus: str | None + WI_meldung_institution: str | None + + +class Grunderfassung_Schulbildung(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + SB_abschluss: str | None + SB_abschlussgrad: str | None + SB_schule: str | None + SB_ort: str | None + SB_land: str | None + SB_abschlussjahr: str | None + SB_bemerkungsfeld: str | None + + +class Grunderfassung_HoehereBildung(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + HB_anerkennung: str | None + HB_abschlussgrad: str | None + HB_abschlussgrad_dokument: str | None + HB_organisation: str | None + HB_beruf: str | None + HB_land: str | None + HB_ort: str | None + HB_abschlussjahr: str | None + HB_bemerkungsfeld: str | None + + +class Grunderfassung_Arbeitserfahrung(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + AE_branche: str | None + AE_bezeichnung: str | None + AE_funktion: str | None + AE_unternehmen: str | None + AE_land: str | None + AE_zeitspanne: str | None + AE_beschaeftigungsart: str | None + AE_bemerkungsfeld: str | None + + +class Grunderfassung_Sprachen(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + SP_sprache: str | None + SP_niveau: str | None + SP_nachweis: str | None + SP_art_nachweis: str | None = None + SP_datum_nachweis: datetime.date | None = None diff --git a/src/wce_crm/form_defs.py b/src/wce_crm/form_defs.py new file mode 100644 index 0000000..365827c --- /dev/null +++ b/src/wce_crm/form_defs.py @@ -0,0 +1,1109 @@ +from __future__ import annotations + +import dataclasses as dc +import enum +import uuid +from collections.abc import Sequence +from typing import Any, Final + +import babel + +from wce_crm.custom_widget_registry import CUSTOM_WIDGET_NAMES + + +class FormFieldType(enum.StrEnum): + GROUP = enum.auto() + TEXT = enum.auto() + LONGTEXT = enum.auto() + DATE = enum.auto() + DATETIME = enum.auto() + DROPDOWN = enum.auto() + EXTENDED_DROPDOWN = enum.auto() + DYNAMIC_LIST = enum.auto() + DYNAMIC_DROPDOWN_NUMERIC = enum.auto() + DYNAMIC_DROPDOWN_OPTION = enum.auto() + TEXT_SEARCH = enum.auto() + CUSTOM = enum.auto() + TEXT_DATE = enum.auto() + TEXT_DATETIME = enum.auto() + + +@dc.dataclass(slots=True) +class FormField: + label: str + type: FormFieldType + children: Sequence[FormField] = dc.field(default_factory=list) + parent: FormField | None = None + required: bool = False + placeholder: str = "" + fill_value: str = "" + readonly: bool = False + options: dc.InitVar[Sequence[tuple[str, Any]]] = tuple() + dropdown_options: Sequence[DropdownOption] = dc.field(default=tuple(), init=False) + key: str = "" + tooltip: str = "" + info: str = "" + custom_widget: str = "" + init_label: str = dc.field(init=False) + ignore_get_data: bool = False + trigger_value: str = "" + enable_uuid_key: bool = False + + def __post_init__( + self, + options: Sequence[tuple[str, Any]], + ) -> None: + if not self.key and self.enable_uuid_key: + self.key = str(uuid.uuid4()) + + self.label = self.label.strip() + self.init_label = self.label.replace("*", "").replace(":", "") + if not self.label.endswith(":") and self.type is not FormFieldType.GROUP: + self.label += ":" + if self.required: + self.label += "*" + + if self.type is FormFieldType.CUSTOM and not self.custom_widget: + raise ValueError("Custom widget must be named using parameter >custom_widget<") + elif ( + self.type is FormFieldType.CUSTOM + and self.custom_widget not in CUSTOM_WIDGET_NAMES + ): + raise KeyError( + ( + f"Custom widget >{self.custom_widget}< is not a known member " + "of the custom widget registry" + ) + ) + + if self.type in (FormFieldType.DROPDOWN, FormFieldType.EXTENDED_DROPDOWN): + self.dropdown_options = tuple(DropdownOption(op[0], op[1]) for op in options) + + if self.type is FormFieldType.DYNAMIC_DROPDOWN_OPTION and not self.trigger_value: + raise ValueError( + "Dynamic Dropdown Option Widget must have a defined option or decision value" + ) + + if self.children: + self.required = self.required or any((child.required for child in self.children)) + for child in self.children: + child.parent = self + + def enhanced_label( + self, + add_text: str, + ) -> str: + enhanced_label = self.init_label + f" {add_text}" + if not enhanced_label.endswith(":") and self.type is not FormFieldType.GROUP: + enhanced_label += ":" + if self.required: + enhanced_label += "*" + + return enhanced_label + + +@dc.dataclass(slots=True) +class DropdownOption: + label: str + _data: dc.InitVar[Any | None] = None + data: Any = dc.field(init=False) + + def __post_init__( + self, + _data: Any | None, + ) -> None: + if _data is None: + self.data = self.label + else: + self.data = _data + + +@dc.dataclass(slots=True) +class CountryList: + iso_to_country: dict[str, str] + for_dropdown: Sequence[tuple[str, str]] + + +def get_country_list_german() -> CountryList: + locale = babel.Locale("de", "DE") + countries: list[tuple[str, str]] = [] + iso_to_country: dict[str, str] = {} + + for iso_code, country_name in locale.territories.items(): + if len(iso_code) == 2 and not iso_code.isdigit(): + countries.append((country_name, iso_code)) + iso_to_country[iso_code] = country_name + + countries.sort(key=lambda x: x[0]) + + return CountryList( + iso_to_country=iso_to_country, + for_dropdown=tuple(countries), + ) + + +def get_list_germany_states() -> CountryList: + states: list[tuple[str, str]] = [] + short_code_to_name: dict[str, str] = {} + + STATE_LIST: list[tuple[str, str]] = [ + ("Bayern", "BY"), + ("Niedersachen", "NI"), + ("Baden-Württemberg", "BW"), + ("Berlin", "BE"), + ("Brandenburg", "BB"), + ("Bremen", "HB"), + ("Hamburg", "HH"), + ("Hessen", "HE"), + ("Mecklenburg", "MV"), + ("Nordrhein-Westfalen", "NW"), + ("Rheinland-Pfalz", "RP"), + ("Saarland", "SL"), + ("Sachsen", "SN"), + ("Sachsen-Anhalt", "ST"), + ("Schleswig-Holstein", "SH"), + ("Thüringen", "TH"), + ] + STATE_LIST.sort(key=lambda x: x[1]) + + for country_name, code in STATE_LIST: + states.append((country_name, code)) + short_code_to_name[code] = country_name + + return CountryList( + iso_to_country=short_code_to_name, + for_dropdown=tuple(states), + ) + + +COUNTRY_LIST: Final[CountryList] = get_country_list_german() +GERMAN_STATE_LIST: Final[CountryList] = get_list_germany_states() + + +INITREC_COMP_SEARCH_HEAD = [ + FormField( + "Suche", + FormFieldType.EXTENDED_DROPDOWN, + required=True, + key="un_suche", + placeholder="Suche...", + ), + FormField( + "Name Unternehmen/Netzwerkpartner", + FormFieldType.TEXT, + required=False, + key="un_name", + readonly=True, + info="ma_unternehmensname", + ), + FormField( + "Straße", + FormFieldType.TEXT, + required=False, + key="un_straße", + readonly=True, + info="ma_strasse", + ), + FormField( + "Hausnummer", + FormFieldType.TEXT, + required=False, + key="un_hausnummer", + readonly=True, + info="ma_hausnummer", + ), + FormField( + "PLZ", + FormFieldType.TEXT, + required=False, + key="un_PLZ", + readonly=True, + info="ma_plz", + ), + FormField( + "Ort", + FormFieldType.TEXT, + required=False, + key="un_ort", + readonly=True, + info="ma_ort", + ), + FormField( + "Suche Ansprechpartner", + FormFieldType.EXTENDED_DROPDOWN, + required=True, + key="person_suche", + placeholder="Suche...", + ), + FormField( + "Titel", + FormFieldType.TEXT, + required=False, + key="person_titel", + readonly=True, + info="an_titel", + ), + FormField( + "Anrede", + FormFieldType.TEXT, + required=False, + key="person_anrede", + readonly=True, + info="an_anrede", + ), + FormField( + "Name", + FormFieldType.TEXT, + required=False, + key="person_name", + readonly=True, + info="an_nachname", + ), + FormField( + "Vorname", + FormFieldType.TEXT, + required=False, + key="person_vorname", + readonly=True, + info="an_vorname", + ), + FormField( + "Telefon", + FormFieldType.TEXT, + required=False, + key="person_telefon", + readonly=True, + info="an_festnetz", + ), + FormField( + "Mobil", + FormFieldType.TEXT, + required=False, + key="person_mobilfunk", + readonly=True, + info="an_mobil", + ), + FormField( + "E-Mail", + FormFieldType.TEXT, + required=False, + key="person_email", + readonly=True, + info="an_mail", + ), + FormField( + "Funktion im Unternehmen", + FormFieldType.TEXT, + required=False, + key="person_funktion", + readonly=True, + info="an_position", + ), + FormField( + "Wie sind Sie auf uns aufmerksam geworden?", + FormFieldType.DROPDOWN, + required=False, + key="kanal_aufmerksamkeit", + options=[ + ("Agentur für Arbeit", None), + ("Ausländerbehörde", None), + ("Jobcenter", None), + ("Freunde/Familie", None), + ("Anerkennungsstelle", None), + ("Beratungsstelle", None), + ("Internet", None), + ("Arbeitgeber", None), + ("Bildungsdienstleister", None), + ("Welcome-Mappe", None), + ("Newsletter WFE", None), + ("Newsletter RM", None), + ("Sonstiges", None), + ], + ), +] + +INITREC_CONTACT_PERSON = [ + FormField( + "Name Unternehmen/Netzwerkpartner (vorausgefüllt von Suche)", + FormFieldType.TEXT, + key="KP_name_partner", + required=False, + placeholder="Text wird nach gewähltem Unternehmen angezeigt", + readonly=True, + ), + FormField( + "Titel", + FormFieldType.TEXT, + key="KP_titel", + required=False, + tooltip=( + "* nur wenn anrufende Person oder kontaktaufnehmende Person " + "nicht die zu beratende Person ist" + ), + ), + FormField( + "Anrede_Anschrift", + FormFieldType.TEXT, + key="KP_anrede_anschrift", + required=True, + ), + FormField( + "Name", + FormFieldType.TEXT, + key="KP_name", + required=True, + ), + FormField( + "Vorname", + FormFieldType.TEXT, + key="KP_vorname", + required=False, + ), + FormField( + "Festnetznummer", + FormFieldType.TEXT, + key="KP_festnetznummer", + required=False, + ), + FormField( + "Mobilfunknummer", + FormFieldType.TEXT, + key="KP_mobilfunknummer", + required=False, + ), + FormField( + "E-Mail", + FormFieldType.TEXT, + key="KP_email", + required=False, + ), + FormField( + "Funktion/Beziehung zur beratenden Person", + FormFieldType.TEXT, + key="KP_funktion_beziehung", + required=False, + ), + FormField( + "Adresse", + FormFieldType.LONGTEXT, + key="KP_adresse", + required=False, + ), +] + + +INITREC_MASTER_DATA = [ + FormField( + "Titel", + FormFieldType.TEXT, + key="titel", + required=False, + tooltip=( + "* nur wenn anrufende Person oder kontaktaufnehmende Person " + "nicht die zu beratende Person ist" + ), + ), + FormField( + "Anrede", + FormFieldType.TEXT, + key="anrede_anschrift", + required=True, + ), + FormField( + "Name", + FormFieldType.TEXT, + key="name", + required=True, + ), + FormField( + "Vorname", + FormFieldType.TEXT, + key="vorname", + required=False, + ), + FormField( + "Geburtsdatum", + FormFieldType.DATE, + key="geburtsdatum", + required=False, + tooltip=( + "* Wichtig zu erfragen, da u.a. Mindestgehaltsschwelle davon abhängt " + "(Regelung bei Ü45 Jahre)" + ), + ), + FormField( + "Herkunftsland", + FormFieldType.EXTENDED_DROPDOWN, + key="herkunftsland", + required=True, + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, + tooltip=("* Wichtig zu erfragen aufgrund eventueller EU-Freizügigkeitsregelung"), + ), + FormField( + "Staatsangehörigkeit", + FormFieldType.EXTENDED_DROPDOWN, + key="staatsangehoerigkeit", + required=False, + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, + tooltip=("* Wichtig zu erfragen aufgrund eventueller EU-Freizügigkeitsregelung"), + ), + FormField( + "Rückkehrer", + FormFieldType.DROPDOWN, + key="rueckkehrer", + required=False, + options=[("ja", None), ("nein", None)], + tooltip=("* Wichtig zu erfragen aufgrund eventueller EU-Freizügigkeitsregelung"), + ), + FormField( + "Wo befindet sich die Person?", + FormFieldType.DROPDOWN, + key="aufenthaltsort", + required=True, + options=[("Inland", None), ("Ausland EU/EWR", None), ("Ausland Drittstaat", None)], + ), + FormField( + "Straße", + FormFieldType.TEXT, + key="strasse", + required=False, + ), + FormField( + "Hausnummer", + FormFieldType.TEXT, + key="hausnummer", + required=False, + ), + FormField( + "PLZ", + FormFieldType.TEXT, + key="PLZ", + required=False, + ), + FormField( + "Ort", + FormFieldType.TEXT, + key="ort", + required=False, + ), + FormField( + "Bundesland", + FormFieldType.DROPDOWN, + key="bundesland", + required=False, + options=GERMAN_STATE_LIST.for_dropdown, + tooltip=("nur wenn Inland angegeben"), + ), + FormField( + "Land", + FormFieldType.EXTENDED_DROPDOWN, + key="land", + required=False, + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, + ), + FormField( + "Festnetznummer", + FormFieldType.TEXT, + key="festnetznummer", + required=False, + ), + FormField( + "Mobilfunknummer", + FormFieldType.TEXT, + key="mobilfunknummer", + required=False, + ), + FormField( + "E-Mail", + FormFieldType.TEXT, + key="email", + required=False, + ), + FormField( + "Familienstand", + FormFieldType.TEXT, + key="familienstand", + required=False, + tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung", + ), + FormField( + "Anzahl Kinder", + FormFieldType.DYNAMIC_DROPDOWN_NUMERIC, + required=False, + tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung", + key="anzahl_kinder", + children=[ + FormField( + "Anzahl Kinder", + FormFieldType.DROPDOWN, + required=False, + options=[(str(x), None) for x in range(11)], + tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung", + key="anzahl", + children=[ + FormField("Alter Kind", FormFieldType.TEXT, key="alter"), + ], + ), + ], + ), +] + + +INITREC_ADDITIONAL_DATA = [ + FormField( + "Deutsch als Kommunikationssprache", + FormFieldType.DROPDOWN, + required=False, + key="WI_deutsch_sprache", + options=[ + ("nein", None), + ("ja, als Muttersprache", None), + ("ja, als Fremdsprache", None), + ], + ), + FormField( + "Aufenthaltstitel", + FormFieldType.DROPDOWN, + required=False, + key="WI_aufenthaltstitel", + options=[ + ("anerkannter Flüchtling §§ 22 - 26 AufenthG", None), + ("Aufenthaltsgestattung §55 AufenthG", None), + ("Blaue Karte EU § 18g AufenthG", None), + ("BüMA (Bescheinigung über Meldung als Asylsuchender)", None), + ("Duldung § 60 AufenthG", None), + ("bisher kein Aufenthaltstitel", None), + ("Deutscher", None), + ("familiäre Gründe §§ 27 - 36 AufenthG", None), + ("Niederlassungserlaubnis §9 AufenthG", None), + ("Staatsbürger EUR/EWR/CH", None), + ("Aufenthalt für Ausbildung §§ 16 - 17 AufenthG", None), + ("Aufenthalt für Erwerbstätigkeit §§ 18- 21 AufenthG", None), + ("Chancenaufenthaltsrecht §104c AufenthG", None), + ("Sonstiges", None), + ], + tooltip="sofern nicht bekannt, unbedingt einfordern", + ), + FormField( + "Gültigkeit Aufenthaltsstatus", + FormFieldType.DATE, + required=False, + key="WI_gueltigkeit_aufenthaltstitel", + ), + FormField( + "Arbeitsstatus aktuell", + FormFieldType.DROPDOWN, + required=False, + key="WI_arbeitsstatus", + options=[ + ("Arbeitslos", None), + ("Ausbildung/Qualifizierung Inland", None), + ("geringfügig beschäftigt", None), + ("in Anstellung Inland", None), + ("selbstständig Inland", None), + ("Ausbildung/Qualifizierung Ausland", None), + ("in Anstellung Ausland", None), + ("selbstständig Ausland", None), + ], + ), + FormField( + "Gemeldet bei Institutionen ", + FormFieldType.DROPDOWN, + required=False, + key="WI_meldung_institution", + options=[ + ("bei keiner", None), + ("Jobcenter mit Leistungsbezug", None), + ("Jobcenter ohne Leistungsbezug", None), + ("Sozialamt mit Leistungsbezug", None), + ("Sozialamt ohne Leistungsbezug", None), + ("Agentur für Arbeit mit Leistungsbezug", None), + ("Agentur für Arbeit ohne Leistungsbezug", None), + ], + ), +] + + +INITREC_SCHOOL = [ + FormField("Abschluss", FormFieldType.TEXT, required=False, key="SB_abschluss"), + FormField( + "Abschlussgrad laut Dokument", + FormFieldType.TEXT, + required=False, + key="SB_abschlussgrad", + ), + FormField( + "Schule", + FormFieldType.TEXT, + required=False, + key="SB_schule", + ), + FormField( + "Ort", + FormFieldType.TEXT, + required=False, + key="SB_ort", + ), + FormField( + "Land", + FormFieldType.EXTENDED_DROPDOWN, + key="SB_land", + required=False, + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, + ), + FormField("Abschlussjahr", FormFieldType.TEXT, required=False, key="SB_abschlussjahr"), + FormField( + "Bemerkungsfeld", + FormFieldType.TEXT, + required=False, + key="SB_bemerkungsfeld", + ), +] + + +INITREC_HIGHER_EDUCATION = [ + FormField( + "Anerkennung", + FormFieldType.TEXT, + required=False, + key="HB_anerkennung", + ), + FormField( + "Abschlussgrad", + FormFieldType.TEXT, + required=False, + key="HB_abschlussgrad", + tooltip=( + "bitte den Titel eingeben z.B. Doktor, Diplom oder " + "Betriebswirt (Fachschulabschluss)" + ), + ), + FormField( + "Abschlussgrad laut Dokument", + FormFieldType.TEXT, + required=False, + key="HB_abschlussgrad_dokument", + ), + FormField( + "Hochschule / Ausbildungsbetrieb / Berufsschule", + FormFieldType.TEXT, + required=False, + key="HB_organisation", + ), + FormField( + "Beruf / Fachrichtung", + FormFieldType.TEXT, + required=False, + key="HB_beruf", + tooltip=( + "bitte spezifizieren z.B. Allgemeinmedizin, Ingenieur Maschinenbau, " + "technischer Betriebswirt Datenverarbeitung" + ), + ), + FormField( + "Land", + FormFieldType.EXTENDED_DROPDOWN, + key="HB_land", + required=False, + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, + ), + FormField( + "Ort", + FormFieldType.TEXT, + required=False, + key="HB_ort", + ), + FormField( + "Abschlussjahr", + FormFieldType.TEXT, + required=False, + key="HB_abschlussjahr", + ), + FormField( + "Bemerkungsfeld", + FormFieldType.TEXT, + required=False, + key="HB_bemerkungsfeld", + tooltip="z.B. Promotionen oder den Studiengang angeben", + ), +] + + +INITREC_WORK_EXPERIENCE = [ + FormField( + "Branche", + FormFieldType.DROPDOWN, + required=False, + key="AE_branche", + options=[ + (part, None) + for part in ( + "Metallerzeugung & -bearbeitung", + "Elektro, Energie, Chemie", + "IT & Software", + "Kunststoff, Papier, Textil", + "Logistik, Verkehr, Transport", + "Handwerk, Bau, Grüne Berufe", + "Gesundheit & Pflege", + "Tourismus & Gastronomie", + "Handel", + "Bildung & Soziales", + "Entwicklung, Planung, Qualität", + "Administration, Finanzen, Verwaltung", + "Marketing, Design, Vertrieb", + "Einkauf, Lager, Wartung", + "Sonstige", + "Keine Schwerpunkte, branchenübergreifende Rekrutierung", + ) + ], + ), + FormField( + "Berufsbezeichnung/Tätigkeit", + FormFieldType.TEXT, + required=False, + key="AE_bezeichnung", + ), + FormField( + "Funktion", + FormFieldType.DROPDOWN, + required=False, + key="AE_funktion", + options=[ + ("Auszubildender", None), + ("Fachkraft", None), + ("Hilfskraft", None), + ("Akademiker", None), + ("Führungskraft", None), + ("Praktikant", None), + ("FSJ/BFD", None), + ("Elternzeit", None), + ("Sabbatical", None), + ("Sonstiges", None), + ], + ), + FormField( + "Unternehmen", + FormFieldType.TEXT, + required=False, + key="AE_unternehmen", + ), + FormField( + "Land", + FormFieldType.EXTENDED_DROPDOWN, + key="AE_land", + required=False, + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, + ), + FormField( + "Zeitspanne (von ... bis ...)", + FormFieldType.TEXT, + required=False, + key="AE_zeitspanne", + ), + FormField( + "Beschäftsigungsart", + FormFieldType.DROPDOWN, + required=False, + key="AE_beschaeftigungsart", + options=[ + ("Vollzeit", None), + ("Teilzeit", None), + ("Sonstiges", None), + ], + tooltip="Minijob, Praktikum, Wehrdienst, soziale Dienste", + ), + FormField( + "Bemerkungsfeld", + FormFieldType.TEXT, + required=False, + key="AE_bemerkungsfeld", + ), +] + + +INITREC_LANGUAGES = [ + FormField( + "Sprache", + FormFieldType.TEXT, + required=False, + key="SP_sprache", + ), + FormField( + "Niveau", + FormFieldType.DROPDOWN, + required=False, + key="SP_niveau", + options=[ + ("A1", None), + ("A2", None), + ("B1", None), + ("B2", None), + ("C1", None), + ("C2", None), + ], + ), + FormField( + "Nachweis", + FormFieldType.DYNAMIC_DROPDOWN_OPTION, + key="", + trigger_value="vorhanden", + children=[ + FormField( + "Nachweis", + FormFieldType.DROPDOWN, + required=False, + options=[("vorhanden", None), ("nicht vorhanden", None)], + key="SP_nachweis", + children=[ + FormField( + "Art des Nachweises", + FormFieldType.TEXT, + required=False, + key="SP_art_nachweis", + ), + FormField( + "Datum des Nachweises", + FormFieldType.DATE, + required=False, + key="SP_datum_nachweis", + ), + ], + ), + ], + ), +] + + +INITREC_COMP = [ + FormField( + "Ersteintrag Datum", + FormFieldType.TEXT_DATETIME, + required=False, + key="Metadaten_erstellung", + readonly=True, + ignore_get_data=True, + ), + FormField( + "Aktualisierung Datum", + FormFieldType.TEXT_DATETIME, + required=False, + key="Metadaten_aktualisierung", + readonly=True, + ignore_get_data=True, + ), + FormField( + "Aktualisierung Nutzer", + FormFieldType.TEXT, + required=False, + key="Metadaten_nutzer", + readonly=True, + ), + FormField( + "Fallnummer", + FormFieldType.TEXT, + required=True, + key="Grunderfassung_fallnummer", + ), + FormField( + "Notizen", + FormFieldType.LONGTEXT, + required=False, + key="Grunderfassung_notiz", + ), + FormField( + "Suche", + FormFieldType.CUSTOM, + custom_widget="grunderfassung_suche", + key="Partnersuche", + children=INITREC_COMP_SEARCH_HEAD, + ), + FormField( + "Status && Projektrelevanz", + FormFieldType.GROUP, + key="Projektrelevanz", + children=[ + FormField( + "Projektrelevanz", + FormFieldType.DYNAMIC_DROPDOWN_OPTION, + key="", + trigger_value="ja", + children=[ + FormField( + "Relevanz", + FormFieldType.DROPDOWN, + required=True, + options=[("ja", None), ("nein", None)], + key="relevanz", + children=[ + FormField( + "Förderperiode", FormFieldType.TEXT, key="foerderperiode" + ), + ], + ), + ], + ), + ], + ), + FormField( + "Daten Kontaktperson", + FormFieldType.GROUP, + key="Kontaktperson", + children=INITREC_CONTACT_PERSON, + ), + FormField( + "Stammdaten", + FormFieldType.GROUP, + key="Stammdaten", + children=INITREC_MASTER_DATA, + ), + FormField( + "Weitere Informationen", + FormFieldType.GROUP, + key="WeitereInfos", + children=INITREC_ADDITIONAL_DATA, + ), + FormField( + "Schulbildung", + FormFieldType.DYNAMIC_LIST, + children=INITREC_SCHOOL, + key="Schulbildung", + ), + FormField( + "Studium/Ausbildung", + FormFieldType.DYNAMIC_LIST, + children=INITREC_HIGHER_EDUCATION, + key="HoehereBildung", + ), + FormField( + "Arbeitserfahrung", + FormFieldType.DYNAMIC_LIST, + children=INITREC_WORK_EXPERIENCE, + key="Arbeitserfahrung", + ), + FormField( + "Sprachkenntnisse", + FormFieldType.DYNAMIC_LIST, + children=INITREC_LANGUAGES, + key="Sprachkenntnisse", + ), +] + +INITREC_PERSON = [ + FormField( + "Ersteintrag Datum", + FormFieldType.TEXT_DATETIME, + required=False, + key="Metadaten_erstellung", + readonly=True, + ignore_get_data=True, + ), + FormField( + "Aktualisierung Datum", + FormFieldType.TEXT_DATETIME, + required=False, + key="Metadaten_aktualisierung", + readonly=True, + ignore_get_data=True, + ), + FormField( + "Aktualisierung Nutzer", + FormFieldType.TEXT, + required=False, + key="Metadaten_nutzer", + readonly=True, + ), + FormField( + "Wiedereintrittsdatum", + FormFieldType.DATE, + required=False, + key="Metadaten_wiedereintrittsdatum", + readonly=False, + ), + FormField( + "Fallnummer", + FormFieldType.TEXT, + required=True, + key="Grunderfassung_fallnummer", + ), + FormField( + "Notizen", + FormFieldType.LONGTEXT, + required=False, + key="Grunderfassung_notiz", + ), + FormField( + "Status && Projektrelevanz", + FormFieldType.GROUP, + key="Projektrelevanz", + children=[ + FormField( + "Projektrelevanz", + FormFieldType.DYNAMIC_DROPDOWN_OPTION, + key="", + trigger_value="ja", + children=[ + FormField( + "Relevanz", + FormFieldType.DROPDOWN, + required=True, + options=[("ja", None), ("nein", None)], + key="relevanz", + children=[ + FormField( + "Förderperiode", FormFieldType.TEXT, key="foerderperiode" + ), + ], + ), + ], + ), + ], + ), + FormField( + "Daten Kontaktperson", + FormFieldType.GROUP, + key="Kontaktperson", + children=INITREC_CONTACT_PERSON, + ), + FormField( + "Stammdaten", + FormFieldType.GROUP, + key="Stammdaten", + children=INITREC_MASTER_DATA, + ), + FormField( + "Weitere Informationen", + FormFieldType.GROUP, + key="WeitereInfos", + children=INITREC_ADDITIONAL_DATA, + ), + FormField( + "Schulbildung", + FormFieldType.DYNAMIC_LIST, + children=INITREC_SCHOOL, + key="Schulbildung", + ), + FormField( + "Studium/Ausbildung", + FormFieldType.DYNAMIC_LIST, + children=INITREC_HIGHER_EDUCATION, + key="HoehereBildung", + ), + FormField( + "Arbeitserfahrung", + FormFieldType.DYNAMIC_LIST, + children=INITREC_WORK_EXPERIENCE, + key="Arbeitserfahrung", + ), + FormField( + "Sprachkenntnisse", + FormFieldType.DYNAMIC_LIST, + children=INITREC_LANGUAGES, + key="Sprachkenntnisse", + ), +]