From 62e69184d723a7eadf484650508616bca0e8889a Mon Sep 17 00:00:00 2001 From: foefl Date: Mon, 6 Jul 2026 15:57:21 +0200 Subject: [PATCH] major refactor with new state mechanics --- prototypes/tests.py | 2 +- src/wce_crm/backend/backend.py | 176 ++++---- src/wce_crm/gui.py | 753 +++++++++++++++++++++------------ src/wce_crm/types.py | 75 +++- 4 files changed, 636 insertions(+), 370 deletions(-) diff --git a/prototypes/tests.py b/prototypes/tests.py index 1b5102f..4e61039 100644 --- a/prototypes/tests.py +++ b/prototypes/tests.py @@ -44,7 +44,7 @@ for r in res: # %% -backend.front_get_company_list() +backend._main_page_get_company_list() # %% diff --git a/src/wce_crm/backend/backend.py b/src/wce_crm/backend/backend.py index d729aac..d382a6b 100644 --- a/src/wce_crm/backend/backend.py +++ b/src/wce_crm/backend/backend.py @@ -1,70 +1,18 @@ from __future__ import annotations -import dataclasses as dc import datetime -from typing import TYPE_CHECKING, Any, TypedDict, cast +from typing import TYPE_CHECKING, Any, cast import polars as pl import sqlalchemy as sql from wce_crm import db +from wce_crm.constants import TIMEZONE_CEST from wce_crm.logging import logger_back as logger +from wce_crm.types import CompanyInfo, ContactPersonInfo, InitRecType, MainPageEntry if TYPE_CHECKING: - from wce_crm.types import ExtAnId, ExtMaId, InitRecId - - -# // externals -class CompanyInfo(TypedDict): - ma_id: str - wce_id: str - ma_unternehmensname: str - ma_branche: str - ma_strasse: str - ma_hausnummer: str - ma_plz: str - ma_ort: str - ma_plz_postfach: str - ma_postfach: str - ma_website: str - ma_mail: str - ma_telefonnummer: str - ma_faxnummer: str - ma_ersteintrag_datum: str - ma_aktualisierung_datum: str - ma_aktualisierung_nutzer: str - ma_sollprozess: str - ma_auslaendische_mitarbeiter: str - ma_quelle_information: str - ma_bemerkung: str - ma_kontakt: str - ma_schlagworte: str - ma_archiviert: str - - -class ContactPersonInfo(TypedDict): - an_id: str - ma_id: str - wce_id: str - st_id: str - an_sachgebiet: str - an_anrede: str - an_titel: str - an_nachname: str - an_vorname: str - an_position: str - an_mail: str - an_festnetz: str - an_mobil: str - an_faxnummer: str - an_hauptansprechpartner: str - an_anrede_anschrift: str - an_bemerkung: str - an_aktualisierung_datum: str - an_aktualisierung_nutzer: str - an_letztes_kontaktdatum: str - an_ersteintrag_datum: str - an_archiviert: str + from wce_crm.types import ExtAnId, ExtMaId, RecId def _transform_for_gui_output( @@ -156,7 +104,7 @@ def initrec_comp_contact_person_search_get_info( # // internals def initrec_company_insert_initial_recording( data: dict[str, Any], -) -> InitRecId: +) -> RecId: logger.debug("[Call backend] insert_initial_recording") stmt = db.grunderfassung_unternehmen.insert() with db.ENGINE.begin() as conn: @@ -172,7 +120,7 @@ def initrec_company_insert_initial_recording( def initrec_company_update_initial_recording( - id_: InitRecId, + id_: RecId, data: dict[str, Any], ) -> None: logger.debug("[Call backend] update_initial_recording") @@ -184,7 +132,7 @@ def initrec_company_update_initial_recording( def initrec_company_get_initial_recording( - id_: InitRecId, + id_: RecId, ) -> dict[str, Any]: logger.debug("[Call backend] get_initial_recording") stmt = db.grunderfassung_unternehmen.select().where( @@ -203,7 +151,7 @@ def initrec_company_get_initial_recording( def initrec_company_delete_initial_recording( - id_: InitRecId, + id_: RecId, ) -> None: logger.debug("[Call backend] delete_initial_recording") stmt = db.grunderfassung_unternehmen.delete().where( @@ -218,7 +166,7 @@ def initrec_company_delete_initial_recording( def initrec_person_insert_initial_recording( data: dict[str, Any], -) -> InitRecId: +) -> RecId: logger.debug("[Call backend] insert_initial_recording") stmt = db.grunderfassung_personen.insert() with db.ENGINE.begin() as conn: @@ -234,7 +182,7 @@ def initrec_person_insert_initial_recording( def initrec_person_update_initial_recording( - id_: InitRecId, + id_: RecId, data: dict[str, Any], ) -> None: logger.debug("[Call backend] update_initial_recording") @@ -246,7 +194,7 @@ def initrec_person_update_initial_recording( def initrec_person_get_initial_recording( - id_: InitRecId, + id_: RecId, ) -> dict[str, Any]: logger.debug("[Call backend] get_initial_recording") stmt = db.grunderfassung_personen.select().where( @@ -265,7 +213,7 @@ def initrec_person_get_initial_recording( def initrec_person_delete_initial_recording( - id_: InitRecId, + id_: RecId, ) -> None: logger.debug("[Call backend] delete_initial_recording") stmt = db.grunderfassung_personen.delete().where( @@ -278,56 +226,86 @@ def initrec_person_delete_initial_recording( raise KeyError(f"Database ID {id_} not found for deletion") -@dc.dataclass(slots=True) -class FrontpageCompany: - erfassung_id: InitRecId - name: str - Metadaten_aktualisierung: datetime.datetime - is_company: bool - - -# TODO separate functions for companies and person: focus on companies first -def front_get_company_list() -> list[FrontpageCompany]: +# // main page interaction +def _main_page_get_company_list() -> list[MainPageEntry]: logger.debug("[Call backend] get_company_list") stmt = sql.select( db.grunderfassung_unternehmen.c.un_id, db.grunderfassung_unternehmen.c.Partnersuche__un_suche, - # db.grunderfassung_unternehmen.c.Stammdaten__vorname, - # db.grunderfassung_unternehmen.c.Stammdaten__name, db.grunderfassung_unternehmen.c.Metadaten_aktualisierung, - ).order_by(db.grunderfassung_unternehmen.c.Metadaten_aktualisierung.desc()) + ) with db.ENGINE.connect() as conn: res = conn.execute(stmt) - front_page_companies: list[FrontpageCompany] = [] + main_page_companies: list[MainPageEntry] = [] for entry in res.mappings(): - erfassung_id = entry["un_id"] - ma_id = entry["Partnersuche__un_suche"] + rec_id = entry["un_id"] + assert rec_id, "no RecID defined" + ma_id_external = entry["Partnersuche__un_suche"] + assert ma_id_external is not None, "external MA ID is NULL" datetime_akt = cast(datetime.datetime, entry["Metadaten_aktualisierung"]) - datetime_akt = datetime_akt.astimezone() + datetime_akt = datetime_akt.astimezone(TIMEZONE_CEST) - name: str - is_company: bool - if ma_id is not None: - comp_info = initrec_comp_search_get_info(ma_id) - name = comp_info["ma_unternehmensname"] - is_company = True - else: - surname: str = entry["Stammdaten__name"] - first_name: str = entry["Stammdaten__vorname"] - names_to_join = [n for n in (first_name, surname) if n] - name = " ".join(names_to_join) - is_company = False + comp_info = initrec_comp_search_get_info(ma_id_external) + display_name = comp_info["ma_unternehmensname"] - front_page_companies.append( - FrontpageCompany( - erfassung_id=erfassung_id, - name=name, + main_page_companies.append( + MainPageEntry( + rec_id=rec_id, + display_name=display_name, Metadaten_aktualisierung=datetime_akt, - is_company=is_company, + type=InitRecType.COMPANY, ) ) - return front_page_companies + return main_page_companies + + +def _main_page_get_person_list() -> list[MainPageEntry]: + logger.debug("[Call backend] get_company_list") + stmt = sql.select( + db.grunderfassung_personen.c.pers_id, + db.grunderfassung_personen.c.Stammdaten__vorname, + db.grunderfassung_personen.c.Stammdaten__name, + db.grunderfassung_personen.c.Metadaten_aktualisierung, + ) + + with db.ENGINE.connect() as conn: + res = conn.execute(stmt) + + main_page_persons: list[MainPageEntry] = [] + + for entry in res.mappings(): + rec_id = entry["pers_id"] + assert rec_id, "no RecID defined" + datetime_akt = cast(datetime.datetime, entry["Metadaten_aktualisierung"]) + datetime_akt = datetime_akt.astimezone() + + surname: str = entry["Stammdaten__name"] + first_name: str = entry["Stammdaten__vorname"] + names_to_join = [n for n in (first_name, surname) if n] + display_name = " ".join(names_to_join) + + main_page_persons.append( + MainPageEntry( + rec_id=rec_id, + display_name=display_name, + Metadaten_aktualisierung=datetime_akt, + type=InitRecType.PERSON, + ) + ) + + return main_page_persons + + +def main_page_get_entries() -> list[MainPageEntry]: + companies = _main_page_get_company_list() + persons = _main_page_get_person_list() + + all_entries = companies + persons + + all_entries.sort(key=lambda x: x.Metadaten_aktualisierung, reverse=True) + + return all_entries diff --git a/src/wce_crm/gui.py b/src/wce_crm/gui.py index 2b44401..8f02800 100644 --- a/src/wce_crm/gui.py +++ b/src/wce_crm/gui.py @@ -75,6 +75,7 @@ from PySide6.QtWidgets import ( ) import wce_crm.constants +from wce_crm import types from wce_crm.backend import backend from wce_crm.constants import TIMEZONE_CEST from wce_crm.data_models import ( @@ -95,7 +96,7 @@ from wce_crm.logging import ( logger_gui, logger_page_consulting, ) -from wce_crm.types import ExtMaId, InitRecId, UserId +from wce_crm.types import ExtMaId, RecId, UserId if TYPE_CHECKING: ... @@ -222,11 +223,25 @@ class CustomForm(Protocol): def validate_form_data(self) -> list[str]: ... +class Module(Protocol): + def sync_state_to_GUI(self) -> None: ... + + def sync_GUI_to_state(self) -> None: ... + + def validate(self) -> None: ... + + def load_state(self, new_state: Any) -> None: ... + + def get_state(self) -> Any: ... + + def save_data(self) -> None: ... + + class AutoFormInsert(Protocol): def __call__( self, data: dict[str, Any], - ) -> backend.InitRecId: ... + ) -> backend.RecId: ... class AutoFormUpdate(Protocol): @@ -263,6 +278,14 @@ class AutoFormConfig: add_buttons: bool = True +@dc.dataclass(slots=True) +class AutoForm_State: + session: Session + cfg: AutoFormConfig + rec_id: RecId | None = None + form_data: dict[str, Any] | None = None + + class CustomWidget(QWidget): def __init__( self, @@ -284,6 +307,74 @@ class CustomWidget(QWidget): def validate_form_data(self) -> list[str]: ... +@dc.dataclass(slots=True) +class Session: + user_id: UserId + user_name: str + + +@dc.dataclass(slots=True) +class Page_NewInitRec_State: + session: Session + + +@dc.dataclass(slots=True) +class Page_InitRecCompany_State: + session: Session + un_id: RecId | None = None + + +@dc.dataclass(slots=True) +class Page_InitRecPerson_State: + session: Session + pers_id: RecId | None = None + + +class ConsultingType(enum.StrEnum): + PAUSCHAL = enum.auto() + INDIVIUAL = enum.auto() + + +@dc.dataclass(slots=True) +class Page_Consulting_State: + session: Session + un_id: RecId | None = None + pers_id: RecId | None = None + cons_type: ConsultingType | None = None + cons_sessions: list[Page_Consulting_ConsultingSession_State] = dc.field( + default_factory=list + ) + + +@dc.dataclass(slots=True) +class Page_Consulting_ConsultingSession_State: + nutzer_id: UserId # must be set with the session (always known) + nutzer_name: str # must be set with the session (always known) + beratung_id: int | None = None # known and not known entries + zeitstempel: datetime.datetime = dc.field( + default_factory=lambda: datetime.datetime.now(tz=datetime.UTC) + ) + ansprechpartner: str | None = None + interaction_scheme: str | None = None + topic: str | None = ( + None # TODO should be linked to something, target unclear (need specification by customer) + ) + anmerkungen: str | None = None + rueckmeldung: str | None = None + + +@dc.dataclass(slots=True) +class Page_Consulting_Table_State: + rows: list[Page_Consulting_ConsultingSession] = dc.field(default_factory=list) + row_states: list[Page_Consulting_ConsultingSession_State] = dc.field(default_factory=list) + + +@dc.dataclass(slots=True) +class Page_CompanyProfile_State: + session: Session + rec_id: RecId | None = None + + def _add_widget_to_layout( form_field: FormField[T], widget: QWidget, @@ -944,8 +1035,8 @@ def validate_form_data( continue error_post = True elif isinstance(widget, FlexibleDateTimeInput): - date = widget.get_datetime() - if date and date.isValid(): + datetime = widget.get_datetime() + if datetime and datetime.isValid(): continue error_post = True elif isinstance( @@ -1093,7 +1184,7 @@ class Grunderfassung_SuchWidget(CustomWidget): def fill_out_company( self, - data: backend.CompanyInfo, + data: types.CompanyInfo, ) -> None: for key, widget in self.company_widgets.items(): if key not in data: @@ -1107,7 +1198,7 @@ class Grunderfassung_SuchWidget(CustomWidget): def fill_out_person( self, - data: backend.ContactPersonInfo, + data: types.ContactPersonInfo, ) -> None: for key, widget in self.person_widgets.items(): if key not in data: @@ -1255,12 +1346,11 @@ class AutoForm(QWidget): def __init__( self, - session: Session, - cfg: AutoFormConfig, + state: AutoForm_State, ) -> None: super().__init__() - self.session = session - self.cfg = cfg + self.STATE = state + self.cfg = state.cfg self.setStyleSheet(""" QGroupBox { font-size: 16px; @@ -1310,7 +1400,7 @@ class AutoForm(QWidget): self.main_layout.addWidget(self.registry_button) self.test_button = QPushButton("Initialisiere Laden") - self.test_button.clicked.connect(self.load_data) + self.test_button.clicked.connect(self._load_data) self.test_button.setFixedHeight(35) self.test_button.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed @@ -1319,11 +1409,11 @@ class AutoForm(QWidget): button_get = QPushButton("GET DATA") button_get.setFixedHeight(35) - button_get.clicked.connect(self.get_form_data) + button_get.clicked.connect(self._get_form_data) self.main_layout.addWidget(button_get) button_set = QPushButton("SET DATA") button_set.setFixedHeight(35) - button_set.clicked.connect(self.set_form_data) + button_set.clicked.connect(self._set_form_data) self.main_layout.addWidget(button_set) id_field_layout = QHBoxLayout() @@ -1385,7 +1475,7 @@ class AutoForm(QWidget): self.reset_btn.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) - self.reset_btn.clicked.connect(self.reset_form) + self.reset_btn.clicked.connect(self._reset_form) self.layout_btn.addWidget(self.reset_btn) # delete self.delete_btn = QPushButton("Eintrag löschen (Strg + L)") @@ -1394,11 +1484,9 @@ class AutoForm(QWidget): self.delete_btn.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) - self.delete_btn.clicked.connect(self.delete_data) + self.delete_btn.clicked.connect(self._delete_data) self.layout_btn.addWidget(self.delete_btn) - self.current_id: InitRecId = -1 - logger_auto_form.debug( "Initialised Auto Form Widget. Registry:%s", pformat_registry(self.widget_registry), @@ -1411,9 +1499,9 @@ class AutoForm(QWidget): try: index = int(self.id_field_input.text()) except ValueError: - index = -1 - self.current_id = index - logger_gui.debug("Set index to %d", self.current_id) + index = None + self.STATE.rec_id = index + logger_gui.debug("Set index to %d, new state: %s", index, self.STATE) def _disable_save(self) -> None: self.save_btn.setEnabled(False) @@ -1428,18 +1516,18 @@ class AutoForm(QWidget): self.save_btn.setText(self.save_btn_txt_enabled) def _activation_delete(self) -> None: - if self.current_id > -1: + if self.STATE.rec_id is not None: self.delete_btn.setEnabled(True) else: self.delete_btn.setEnabled(False) - def delete_data(self) -> None: - assert self.current_id > -1, "deletion initialised despite no index set" - self.cfg.data_delete(self.current_id) + def _delete_data(self) -> None: + assert self.STATE.rec_id is not None, "deletion initialised despite no index set" + self.cfg.data_delete(self.STATE.rec_id) self.update_triggered.emit() - self.reset_form() + self._reset_form() - def load_data( + def _load_data( self, lookup_id: int | None = None, ) -> None: @@ -1447,17 +1535,18 @@ class AutoForm(QWidget): return logger_auto_form.info(">>>> LOAD CLICKED") - if lookup_id is None or lookup_id == 0: - lookup_id = self.current_id - self.reset_form() + if lookup_id is None: + # only debugging + lookup_id = self.STATE.rec_id + self._reset_form() logger_auto_form.debug("Lookup ID: %d", lookup_id) - if lookup_id > 0: + if lookup_id is not None and lookup_id > 0: logger_auto_form.debug("Load from DB:") loaded_data = self.cfg.data_get(lookup_id) else: - logger_auto_form.error("Loading: Lookup ID <= 0. Do nothing!") + logger_auto_form.error("Loading: Lookup ID 0 or NULL. Do nothing!") return logger_auto_form.debug( @@ -1469,13 +1558,63 @@ class AutoForm(QWidget): form_data = model.to_gui() logger_auto_form.debug("Set form data...") logger_auto_form.debug("Form data:\n%s", pformat(form_data)) - self.set_form_data(form_data) - self.current_id = lookup_id + self._set_form_data(form_data) + self.STATE.rec_id = lookup_id self._activation_delete() + def _validate_form_data(self) -> list[str]: + return validate_form_data(self.widget_registry) + + def _reset_form(self) -> None: + reset_form(self.widget_registry) + self.STATE.rec_id = None + self._activation_delete() + + def _get_form_data(self) -> dict[str, Any]: + form_data = get_form_data(self.widget_registry) + logger_auto_form.debug("\n\n>>>>>>> [AutoForm] Call get form data:") + logger_auto_form.debug("Form Data:\n%s", pformat(form_data)) + + if DEBUG_GET_SET: + self.debug_form_data = form_data + + return form_data + + def _set_form_data( + self, + data: dict[str, Any], + ) -> None: + logger_auto_form.debug("\n\n>>>>>>> [AutoForm] Call set form data:") + if DEBUG_GET_SET: + data = self.debug_form_data + + set_form_data(self.widget_registry, data) + + def sync_state_to_GUI(self) -> None: + self._load_data(self.STATE.rec_id) + + def sync_GUI_to_state(self) -> None: + form_data = self._get_form_data() + self.STATE.form_data = form_data + + def validate(self) -> None: ... + + def load_state( + self, + new_state: AutoForm_State, + ) -> None: + self.STATE.rec_id = new_state.rec_id + self.STATE.form_data = new_state.form_data + self.sync_state_to_GUI() + + def get_state(self) -> AutoForm_State: + self.sync_GUI_to_state() + + return self.STATE + def save_data(self) -> None: self._disable_save() - errors = self.validate_form_data() + errors = self._validate_form_data() if errors: # errors: abort saving and show pop up window @@ -1490,7 +1629,7 @@ class AutoForm(QWidget): # no errors: data can be saved logger_auto_form.info("Erfolg! Alle Daten sind valide.") logger_auto_form.info("Get form data call...") - form_data = self.get_form_data() + form_data = self._get_form_data() logger_auto_form.debug( "\n\n------------>>>>>>>>> Get form data\n%s", pformat(form_data) @@ -1533,14 +1672,14 @@ class AutoForm(QWidget): pformat(db_data), ) - if self.current_id < 0: + if self.STATE.rec_id is None: logger_auto_form.debug("Insert triggered") init_rec_id = self.cfg.data_insert(db_data) assert isinstance(init_rec_id, int) - self.current_id = init_rec_id + self.STATE.rec_id = init_rec_id else: logger_auto_form.debug("Update triggered") - self.cfg.data_update(self.current_id, db_data) + self.cfg.data_update(self.STATE.rec_id, db_data) logger_auto_form.info("Data saved successfully") self.update_triggered.emit() @@ -1549,34 +1688,6 @@ class AutoForm(QWidget): # always re-enable save, even if error occurred self._enable_save() - def validate_form_data(self) -> list[str]: - return validate_form_data(self.widget_registry) - - def reset_form(self) -> None: - reset_form(self.widget_registry) - self.current_id = -1 - self._activation_delete() - - def get_form_data(self) -> dict[str, Any]: - form_data = get_form_data(self.widget_registry) - logger_auto_form.debug("\n\n>>>>>>> [AutoForm] Call get form data:") - logger_auto_form.debug("Form Data:\n%s", pformat(form_data)) - - if DEBUG_GET_SET: - self.debug_form_data = form_data - - return form_data - - def set_form_data( - self, - data: dict[str, Any], - ) -> None: - logger_auto_form.debug("\n\n>>>>>>> [AutoForm] Call set form data:") - if DEBUG_GET_SET: - data = self.debug_form_data - - set_form_data(self.widget_registry, data) - class FlexibleDateInput(QWidget): def __init__(self, parent=None): @@ -2454,15 +2565,15 @@ class NoScrollFilter(QObject): return super().eventFilter(obj, event) -class ClickableCell(QFrame): +class MainPage_ClickableCell(QFrame): """cell in the table on the startup screen""" - clicked = Signal(backend.FrontpageCompany) + clicked = Signal(types.MainPageEntry) def __init__( self, text: str, - data_record: backend.FrontpageCompany, + data_record: types.MainPageEntry, ): super().__init__() self.data_record = data_record @@ -2488,7 +2599,7 @@ class ClickableCell(QFrame): self.clicked.emit(self.data_record) -class HeaderCell(QLabel): +class MainPage_HeaderCell(QLabel): def __init__( self, text: str, @@ -2508,19 +2619,19 @@ class HeaderCell(QLabel): """) -class NewEntrySelect_view(QWidget): +class Page_NewInitRec(QWidget): """view for new entry for initial recording ("Grunderfassung")""" back_requested = Signal() # Signal back button - company_requested = Signal(bool) # Signal "Unternehmen" (bool: reset yes/no) - person_requested = Signal() # Signal "Individualperson" + company_requested = Signal(Page_InitRecCompany_State) # Signal "Unternehmen" + person_requested = Signal(Page_InitRecPerson_State) # Signal "Individualperson" def __init__( self, - session: Session, + state: Page_NewInitRec_State, ): super().__init__() - self.session = session + self.STATE = state layout = QVBoxLayout(self) layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft) @@ -2539,15 +2650,12 @@ class NewEntrySelect_view(QWidget): btn_company = QPushButton("Unternehmen →") btn_company.setFixedWidth(300) btn_company.setFixedHeight(40) - btn_company.clicked.connect(lambda: self.company_requested.emit(True)) + btn_company.clicked.connect(self._request_initrec_company) btn_person = QPushButton("Individualperson →") btn_person.setFixedWidth(300) btn_person.setFixedHeight(40) - btn_person.clicked.connect(lambda: self.person_requested.emit()) - # btn_person.clicked.connect( - # lambda: logger_gui.info("[Grunderfassung: Dummy Individualperson] Person gewählt") - # ) + btn_person.clicked.connect(self._request_initrec_person) layout.addWidget(back_btn) layout.addSpacing(15) @@ -2555,6 +2663,44 @@ class NewEntrySelect_view(QWidget): layout.addWidget(btn_company) layout.addWidget(btn_person) + def _request_initrec_company(self) -> None: + req_state = Page_InitRecCompany_State( + session=self.STATE.session, + un_id=None, + ) + logger_gui.debug("[Page -- InitRec Company] State to call: %s", req_state) + self.company_requested.emit(req_state) + + def _request_initrec_person(self) -> None: + req_state = Page_InitRecPerson_State( + session=self.STATE.session, + pers_id=None, + ) + logger_gui.debug("[Page -- InitRec Company] State to call: %s", req_state) + self.person_requested.emit(req_state) + + def sync_state_to_GUI(self) -> None: + pass + + def sync_GUI_to_state(self) -> None: + pass + + def validate(self) -> None: + pass + + def load_state( + self, + new_state: Page_NewInitRec_State, + ) -> None: + self.sync_state_to_GUI() + + def get_state(self) -> Page_NewInitRec_State: + self.sync_GUI_to_state() + return self.STATE + + def save_data(self) -> None: + pass + CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig( model=Grunderfassung_Unternehmen, @@ -2590,15 +2736,15 @@ CUSTOM_WIDGETS: Final[dict[str, type[CustomWidget]]] = { class Page_InitRecCompany(QWidget): back_main_requested = Signal() # back to main page back_requested = Signal() # back button - update_triggered = Signal() # form saved (data changed for front page) - company_profile_requested = Signal(InitRecId) + update_main_page = Signal() # form saved (data changed for front page) + company_profile_requested = Signal(Page_CompanyProfile_State) def __init__( self, - session: Session, + state: Page_InitRecCompany_State, ): super().__init__() - self.session = session + self.STATE = state # Hauptlayout der Seite outer_layout = QHBoxLayout(self) @@ -2620,9 +2766,7 @@ class Page_InitRecCompany(QWidget): header_layout.setContentsMargins(0, 0, 0, 10) comp_profile_btn = QPushButton("Unternehmensprofil") - comp_profile_btn.clicked.connect( - lambda: self.company_profile_requested.emit(self.current_id) - ) + comp_profile_btn.clicked.connect(self._request_company_profile) comp_profile_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) comp_profile_btn.setMinimumWidth(250) # comp_profile_btn.setMaximumWidth(200) @@ -2668,9 +2812,14 @@ class Page_InitRecCompany(QWidget): # --- AUTO FORM LAYOUT --- container_layout.addSpacing(20) - self.auto_form = AutoForm(self.session, cfg=CONFIG_GRUNDERFASSUNG_UNTERNEHMEN) + self.AUTO_FORM_CFG = CONFIG_GRUNDERFASSUNG_UNTERNEHMEN + auto_form_state = AutoForm_State( + session=self.STATE.session, + cfg=self.AUTO_FORM_CFG, + ) + self.auto_form = AutoForm(auto_form_state) container_layout.addWidget(self.auto_form) - self.auto_form.update_triggered.connect(lambda: self.update_triggered.emit()) + self.auto_form.update_triggered.connect(lambda: self.update_main_page.emit()) container_layout.addSpacing(15) @@ -2730,13 +2879,50 @@ class Page_InitRecCompany(QWidget): # self.selection_county.style().polish(self.selection_county) # self.selection_county.update() + def _request_company_profile(self) -> None: + req_state = Page_CompanyProfile_State( + session=self.STATE.session, + rec_id=self.STATE.un_id, + ) + logger_gui.debug("[Page -- InitRec Company] State to call: %s", req_state) + self.company_profile_requested.emit(req_state) + def reset_form(self) -> None: - self.auto_form.reset_form() + self.auto_form._reset_form() @property - def current_id(self) -> InitRecId: + def current_id(self) -> RecId: return self.auto_form.current_id + def sync_state_to_GUI(self) -> None: + auto_form_state = AutoForm_State( + session=self.STATE.session, + cfg=self.AUTO_FORM_CFG, + rec_id=self.STATE.un_id, + form_data=None, + ) + self.auto_form.load_state(auto_form_state) + + def sync_GUI_to_state(self) -> None: + self.auto_form.sync_GUI_to_state() + + def validate(self) -> None: ... + + def load_state( + self, + new_state: Page_InitRecCompany_State, + ) -> None: + self.STATE.un_id = new_state.un_id + + self.sync_state_to_GUI() + + def get_state(self) -> Page_InitRecCompany_State: + self.sync_GUI_to_state() + return self.STATE + + def save_data(self) -> None: + self.auto_form.save_data() + class Page_InitRecPerson(QWidget): back_main_requested = Signal() # back to main page @@ -2745,10 +2931,10 @@ class Page_InitRecPerson(QWidget): def __init__( self, - session: Session, + state: Page_InitRecPerson_State, ): super().__init__() - self.session = session + self.STATE = state # Hauptlayout der Seite outer_layout = QHBoxLayout(self) vert_layout = QVBoxLayout() @@ -2807,7 +2993,9 @@ class Page_InitRecPerson(QWidget): # --- AUTO FORM LAYOUT --- container_layout.addSpacing(20) - self.auto_form = AutoForm(self.session, cfg=CONFIG_GRUNDERFASSUNG_PERSONEN) + self.AUTO_FORM_CFG = CONFIG_GRUNDERFASSUNG_PERSONEN + auto_form_state = AutoForm_State(session=self.STATE.session, cfg=self.AUTO_FORM_CFG) + self.auto_form = AutoForm(auto_form_state) container_layout.addWidget(self.auto_form) self.auto_form.update_triggered.connect(lambda: self.update_triggered.emit()) @@ -2869,39 +3057,57 @@ class Page_InitRecPerson(QWidget): self.selection_county.update() def reset_form(self) -> None: - self.auto_form.reset_form() + self.auto_form._reset_form() + def sync_state_to_GUI(self) -> None: + auto_form_state = AutoForm_State( + session=self.STATE.session, + cfg=self.AUTO_FORM_CFG, + rec_id=self.STATE.pers_id, + form_data=None, + ) + self.auto_form.load_state(auto_form_state) -class ConsultingType(enum.StrEnum): - PAUSCHAL = enum.auto() - INDIVIUAL = enum.auto() + def sync_GUI_to_state(self) -> None: + self.auto_form.sync_GUI_to_state() + def validate(self) -> None: ... -@dc.dataclass(slots=True, kw_only=True) -class ConsultingRequest: - cons_type: ConsultingType - un_id: InitRecId | None = None - pers_id: InitRecId | None = None + def load_state( + self, + new_state: Page_InitRecPerson_State, + ) -> None: + self.STATE.pers_id = new_state.pers_id + + self.sync_state_to_GUI() + + def get_state(self) -> Page_InitRecPerson_State: + self.sync_GUI_to_state() + return self.STATE + + def save_data(self) -> None: + self.auto_form.save_data() class Page_CompanyProfile(QWidget): back_main_requested = Signal() # back to main page - back_requested = Signal(bool) # back to init rec (bool: reset yes/no) + back_requested = Signal( + Page_InitRecCompany_State + ) # back to init rec (bool: reset yes/no) # TODO update should trigger something else # more like listening to 'child' ("Beratungen") # update_triggered = Signal() # form saved (data changed for front page) - consulting_requested = Signal(ConsultingRequest) + consulting_requested = Signal(Page_Consulting_State) def __init__( self, - session: Session, + state: Page_CompanyProfile_State, ): super().__init__() - self.session = session + self.STATE = state # state management - self.current_id: InitRecId = -1 - self.COMPANY_FETAURES_MASTER_DATA: dict[str, str] = { + self.COMPANY_FEATURES_MASTER_DATA: dict[str, str] = { "ma_unternehmensname": "Name:", "ma_branche": "Branche:", "ma_strasse": "Straße:", @@ -2938,7 +3144,7 @@ class Page_CompanyProfile(QWidget): back_btn_main.setMinimumWidth(200) back_btn_main.setMaximumWidth(200) back_btn_step = QPushButton("← Zurück") - back_btn_step.clicked.connect(lambda: self.back_requested.emit(False)) + back_btn_step.clicked.connect(self._request_initrec_company) back_btn_step.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) back_btn_step.setMinimumWidth(200) back_btn_step.setMaximumWidth(200) @@ -2993,7 +3199,7 @@ class Page_CompanyProfile(QWidget): container_layout.addSpacing(30) - self.title_company_name = HeaderCell("Unternehmen: n.v.", font_size=16) + self.title_company_name = MainPage_HeaderCell("Unternehmen: n.v.", font_size=16) container_layout.addWidget(self.title_company_name) container_layout.addSpacing(20) @@ -3009,13 +3215,13 @@ class Page_CompanyProfile(QWidget): left_frame.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum) left_layout = QVBoxLayout(left_frame) main_hor_layout.addWidget(left_frame, stretch=1, alignment=Qt.AlignmentFlag.AlignTop) - master_data_label = HeaderCell("Stammdaten & Vermerke") + master_data_label = MainPage_HeaderCell("Stammdaten & Vermerke") master_data_label.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) left_layout.addWidget(master_data_label) # self.table_master_data = MasterDataTable() - default_labels = tuple(self.COMPANY_FETAURES_MASTER_DATA.values()) + default_labels = tuple(self.COMPANY_FEATURES_MASTER_DATA.values()) self.table_master_data = MasterDataFormWidget(default_labels) left_layout.addWidget(self.table_master_data) left_layout.addWidget(QLabel("PLATZHALTER Vermerke: Welche Notizen?")) @@ -3032,20 +3238,20 @@ class Page_CompanyProfile(QWidget): QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum ) top_right_layout = QVBoxLayout(top_right_frame) - common_consultancy_label = HeaderCell("Pauschalberatung") + common_consultancy_label = MainPage_HeaderCell("Pauschalberatung") common_consultancy_label.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) top_right_layout.addWidget(common_consultancy_label) - top_right_new_btn = QPushButton("+ Hinzufügen") - top_right_new_btn.clicked.connect( + self.add_btn_pauschal = QPushButton("+ Hinzufügen") + self.add_btn_pauschal.clicked.connect( lambda: self._request_consulting(ConsultingType.PAUSCHAL) ) - top_right_new_btn.setSizePolicy( + self.add_btn_pauschal.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) - top_right_new_btn.setMinimumHeight(30) - top_right_layout.addWidget(top_right_new_btn) + self.add_btn_pauschal.setMinimumHeight(30) + top_right_layout.addWidget(self.add_btn_pauschal) # bottom right area bottom_right_frame = QFrame() @@ -3054,20 +3260,20 @@ class Page_CompanyProfile(QWidget): QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum ) bottom_right_layout = QVBoxLayout(bottom_right_frame) - individual_consultancy_label = HeaderCell("Individualberatung") + individual_consultancy_label = MainPage_HeaderCell("Individualberatung") individual_consultancy_label.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) bottom_right_layout.addWidget(individual_consultancy_label) - bottom_right_new_btn = QPushButton("+ Hinzufügen") - bottom_right_new_btn.clicked.connect( + self.add_btn_individual = QPushButton("+ Hinzufügen") + self.add_btn_individual.clicked.connect( lambda: self._request_consulting(ConsultingType.INDIVIUAL) ) - bottom_right_new_btn.setSizePolicy( + self.add_btn_individual.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed ) - bottom_right_new_btn.setMinimumHeight(30) - bottom_right_layout.addWidget(bottom_right_new_btn) + self.add_btn_individual.setMinimumHeight(30) + bottom_right_layout.addWidget(self.add_btn_individual) right_column_layout.addWidget(top_right_frame) right_column_layout.addWidget(bottom_right_frame) @@ -3108,16 +3314,74 @@ class Page_CompanyProfile(QWidget): # } # """) + def _request_initrec_company(self) -> None: + req_state = Page_InitRecCompany_State( + session=self.STATE.session, + un_id=self.STATE.rec_id, + ) + logger_gui.debug("[Page -- InitRec Company] State to call: %s", req_state) + self.back_requested.emit(req_state) + def _request_consulting( self, cons_type: ConsultingType, ) -> None: - assert self.current_id > -1, ( + assert self.STATE.rec_id is not None, ( "tried to call consulting from comp profile without set ID" ) - # TODO - req = ConsultingRequest(cons_type=cons_type, un_id=self.current_id) - self.consulting_requested.emit(req) + new_state = Page_Consulting_State( + session=self.STATE.session, un_id=self.STATE.rec_id, cons_type=cons_type + ) + self.consulting_requested.emit(new_state) + + def _disable_add_btns(self) -> None: + self.add_btn_pauschal.setEnabled(False) + self.add_btn_individual.setEnabled(False) + + def _enable_add_btns(self) -> None: + self.add_btn_pauschal.setEnabled(True) + self.add_btn_individual.setEnabled(True) + + def sync_state_to_GUI(self) -> None: + if self.STATE.rec_id is None: + # just clear and do nothing else + logger_gui.debug("just reset, ID is None") + self.table_master_data.reset() + self.set_company_name("") + self._disable_add_btns() + return + + self._enable_add_btns() + init_rec = backend.initrec_company_get_initial_recording(self.STATE.rec_id) + ma_id = cast(ExtMaId, init_rec["Partnersuche__un_suche"]) + assert ma_id is not None, "no ma_id obtained" + comp_info = backend.initrec_comp_search_get_info(ma_id) + + data: dict[str, Any] = {} + + for feature, value in comp_info.items(): + if feature in self.COMPANY_FEATURES_MASTER_DATA: + data[self.COMPANY_FEATURES_MASTER_DATA[feature]] = value + + self.table_master_data.load_data(data) + self.set_company_name(data["Name:"]) + + def sync_GUI_to_state(self) -> None: ... + + def validate(self) -> None: ... + + def load_state( + self, + new_state: Page_CompanyProfile_State, + ) -> None: + logger_gui.debug("Called function to load with ID: %s", new_state.rec_id) + self.STATE.rec_id = new_state.rec_id + + self.sync_state_to_GUI() + + def get_state(self) -> Any: ... + + def save_data(self) -> None: ... def set_company_name(self, name: str) -> None: if not name: @@ -3128,41 +3392,41 @@ class Page_CompanyProfile(QWidget): txt = f"Unternehmen: {name}" self.title_company_name.setText(txt) - def load_from_id( - self, - id_: InitRecId, - ) -> None: - assert id_ is not None, "tried to load with InitRecId None" - assert id_, "tried to load with invalid InitRecId" - logger_gui.debug(f"Called function to load with ID: {id_}") + # def load_from_id( + # self, + # id_: InitRecId, + # ) -> None: + # assert id_ is not None, "tried to load with InitRecId None" + # assert id_, "tried to load with invalid InitRecId" + # logger_gui.debug(f"Called function to load with ID: {id_}") - if id_ < 0: - # just clear and do nothing else - logger_gui.debug("just reset with ID < 0") - self.table_master_data.reset() - self.set_company_name("") - self.current_id = id_ - return + # if id_ < 0: + # # just clear and do nothing else + # logger_gui.debug("just reset with ID < 0") + # self.table_master_data.reset() + # self.set_company_name("") + # self.current_id = id_ + # return - if id_ == self.current_id: - logger_gui.debug("ID did not change. Do nothing") - self.current_id = id_ - return + # if id_ == self.current_id: + # logger_gui.debug("ID did not change. Do nothing") + # self.current_id = id_ + # return - init_rec = backend.initrec_company_get_initial_recording(id_) - ma_id = cast(ExtMaId, init_rec["Partnersuche__un_suche"]) - assert ma_id is not None, "no ma_id obtained" - comp_info = backend.initrec_comp_search_get_info(ma_id) + # init_rec = backend.initrec_company_get_initial_recording(id_) + # ma_id = cast(ExtMaId, init_rec["Partnersuche__un_suche"]) + # assert ma_id is not None, "no ma_id obtained" + # comp_info = backend.initrec_comp_search_get_info(ma_id) - data: dict[str, Any] = {} + # data: dict[str, Any] = {} - for feature, value in comp_info.items(): - if feature in self.COMPANY_FETAURES_MASTER_DATA: - data[self.COMPANY_FETAURES_MASTER_DATA[feature]] = value + # for feature, value in comp_info.items(): + # if feature in self.COMPANY_FEATURES_MASTER_DATA: + # data[self.COMPANY_FEATURES_MASTER_DATA[feature]] = value - self.table_master_data.load_data(data) - self.set_company_name(data["Name:"]) - self.current_id = id_ + # self.table_master_data.load_data(data) + # self.set_company_name(data["Name:"]) + # self.current_id = id_ class MasterDataTable(QWidget): @@ -3342,46 +3606,6 @@ class MasterDataFormWidget(QWidget): """) -@dc.dataclass(slots=True) -class Session: - user_id: UserId - user_name: str - - -@dc.dataclass(slots=True) -class Page_Consulting_ConsultingSession_State: - nutzer_id: UserId # must be set with the session (always known) - nutzer_name: str # must be set with the session (always known) - beratung_id: int | None = None # known and not known entries - zeitstempel: datetime.datetime = dc.field( - default_factory=lambda: datetime.datetime.now(tz=datetime.UTC) - ) - ansprechpartner: str | None = None - interaction_scheme: str | None = None - topic: str | None = ( - None # TODO should be linked to something, target unclear (need specification by customer) - ) - anmerkungen: str | None = None - rueckmeldung: str | None = None - - -@dc.dataclass(slots=True) -class Page_Consulting_Table_State: - rows: list[Page_Consulting_ConsultingSession] = dc.field(default_factory=list) - row_states: list[Page_Consulting_ConsultingSession_State] = dc.field(default_factory=list) - - -@dc.dataclass(slots=True) -class Page_Consulting_State: - session: Session - un_id: InitRecId | None = None - pers_id: InitRecId | None = None - cons_type: ConsultingType | None = None - cons_sessions: list[Page_Consulting_ConsultingSession_State] = dc.field( - default_factory=list - ) - - class Page_Consulting(QWidget): back_main_requested = Signal() # back to main page back_requested = Signal(bool) # back to init rec (bool: reset yes/no) @@ -3604,14 +3828,14 @@ class Page_Consulting(QWidget): self.table_sessions.get_data() # TODO placeholder, check if needed - def _sync_state_to_ui(self) -> None: + def sync_state_to_GUI(self) -> None: match self.STATE.cons_type: case ConsultingType.PAUSCHAL: self.type_pauschal_btn.setChecked(True) case ConsultingType.INDIVIUAL: self.type_individual_btn.setChecked(True) - def _sync_ui_to_state(self) -> None: ... + def sync_GUI_to_state(self) -> None: ... def type_consulting_changed( self, @@ -3630,18 +3854,18 @@ class Page_Consulting(QWidget): logger_gui.debug("[Consulting Page] Selected cons_type: %s", cons_type) self.STATE.cons_type = cons_type - def load_from_request( + def load_from_state( self, - req: ConsultingRequest, + new_state: Page_Consulting_State, ) -> None: # TODO add correct loading behaviour # TODO add validation (correct type with necessary information) - logger_gui.debug("[Consulting] Loading with request:\n%s", req) - self.STATE.un_id = req.un_id - self.STATE.pers_id = req.pers_id - self.STATE.cons_type = req.cons_type + logger_gui.debug("[Consulting] Loading with request:\n%s", new_state) + self.STATE.un_id = new_state.un_id + self.STATE.pers_id = new_state.pers_id + self.STATE.cons_type = new_state.cons_type - self._sync_state_to_ui() + self.sync_state_to_GUI() class Page_Consulting_ConsultingSession(QWidget): @@ -3736,9 +3960,9 @@ class Page_Consulting_ConsultingSession(QWidget): layout.setStretch(i, stretch) self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) - self._sync_state_to_gui() # ?? only needed if entry is from database? + self.sync_state_to_gui() # ?? only needed if entry is from database? - def _sync_state_to_gui(self) -> None: + def sync_state_to_gui(self) -> None: # TODO set correct timestamp logger_page_consulting.debug( "[Page Consulting -- Consulting Session]: State = %s", self.STATE @@ -3751,8 +3975,7 @@ class Page_Consulting_ConsultingSession(QWidget): set_widget_value(self.plain_text, self.STATE.anmerkungen) set_widget_value(self.response, self.STATE.rueckmeldung) - def _sync_gui_to_state(self) -> None: - # TODO: get timestamp from custom field self.STATE.timestamp + def sync_gui_to_state(self) -> None: self.STATE.zeitstempel = self.timestamp.get_pydatetime() self.STATE.ansprechpartner = get_widget_value(self.contact) self.STATE.interaction_scheme = get_widget_value(self.contact_type) @@ -3761,7 +3984,7 @@ class Page_Consulting_ConsultingSession(QWidget): self.STATE.rueckmeldung = get_widget_value(self.response) def get_data(self) -> Page_Consulting_ConsultingSession_State: - self._sync_gui_to_state() + self.sync_gui_to_state() return self.STATE @@ -3963,11 +4186,11 @@ class Page_Consulting_Table(QWidget): while self.STATE.rows: self.remove_row(self.STATE.rows[0]) - def _sync_state_to_gui(self) -> None: + def sync_state_to_gui(self) -> None: for row_state in self.STATE.row_states: self.add_row(row_state) - def _sync_gui_to_state(self) -> None: + def sync_gui_to_state(self) -> None: row_data: list[Page_Consulting_ConsultingSession_State] = [] for i in range(self.container_layout.count() - 1): @@ -3984,7 +4207,7 @@ class Page_Consulting_Table(QWidget): def get_data(self) -> Page_Consulting_Table_State: logger_page_consulting.debug("[Page Consulting -- Table] Syncing GUI to state...") - self._sync_gui_to_state() + self.sync_gui_to_state() logger_page_consulting.debug("[Page Consulting -- Table]: State = %s", self.STATE) @@ -4037,26 +4260,30 @@ class MainWindow(QMainWindow): self.stack.addWidget(self.main_page) # SITE: add new entries for 'Grunderfassung' - self.new_entry_select = NewEntrySelect_view(self.session) - self.new_entry_select.back_requested.connect(self.show_main_page) - self.new_entry_select.company_requested.connect(self.show_page_initrec_company) - self.new_entry_select.person_requested.connect(self.show_page_initrec_person) - self.stack.addWidget(self.new_entry_select) + new_initrec_select_state = Page_NewInitRec_State(session=self.session) + self.new_initrec_select = Page_NewInitRec(new_initrec_select_state) + self.new_initrec_select.back_requested.connect(self.show_main_page) + self.new_initrec_select.company_requested.connect(self.show_page_initrec_company) + self.new_initrec_select.person_requested.connect(self.show_page_initrec_person) + self.stack.addWidget(self.new_initrec_select) # SITE: 'Grunderfassung Unternehmen' - self.initrec_company = Page_InitRecCompany(self.session) + initrec_company_state = Page_InitRecCompany_State(session=self.session) + self.initrec_company = Page_InitRecCompany(initrec_company_state) self.initrec_company.back_main_requested.connect(self.show_main_page) self.initrec_company.back_requested.connect(self.show_new_entry_select) - self.initrec_company.update_triggered.connect(self.update_grid) + self.initrec_company.update_main_page.connect(self.update_grid) self.initrec_company.company_profile_requested.connect(self.show_page_company_profile) self.stack.addWidget(self.initrec_company) # SITE: 'Grunderfassung Person' - self.initrec_person = Page_InitRecPerson(self.session) + initrec_person_state = Page_InitRecPerson_State(session=self.session) + self.initrec_person = Page_InitRecPerson(initrec_person_state) self.initrec_person.back_main_requested.connect(self.show_main_page) self.initrec_person.back_requested.connect(self.show_new_entry_select) self.initrec_person.update_triggered.connect(self.update_grid) self.stack.addWidget(self.initrec_person) # SITE: 'Unternehmensprofil' - self.company_profile = Page_CompanyProfile(self.session) + comp_profile_state = Page_CompanyProfile_State(session=self.session) + self.company_profile = Page_CompanyProfile(comp_profile_state) self.company_profile.back_main_requested.connect(self.show_main_page) self.company_profile.back_requested.connect(self.show_page_initrec_company) self.company_profile.consulting_requested.connect(self.show_page_consulting) @@ -4152,9 +4379,9 @@ class MainWindow(QMainWindow): "Datum", ] for col_idx, title in enumerate(headers): - self.header_grid.addWidget(HeaderCell(title), self.current_row, col_idx) + self.header_grid.addWidget(MainPage_HeaderCell(title), self.current_row, col_idx) - self.update_grid() + # self.update_grid() return main_widget @@ -4164,60 +4391,46 @@ class MainWindow(QMainWindow): def update_grid(self) -> None: clear_layout(self.grid) - data = backend.front_get_company_list() + data = backend.main_page_get_entries() for entry in data: self.add_row_to_grid(entry) def add_row_to_grid( self, - entry: backend.FrontpageCompany, + entry: types.MainPageEntry, ): row = self.current_row # NAME - cell_name = ClickableCell(entry.name, entry) + cell_name = MainPage_ClickableCell(entry.display_name, entry) cell_name.clicked.connect(self.goto_initial_recording) self.grid.addWidget(cell_name, row, 0) # DATE cell_date_name = entry.Metadaten_aktualisierung.date().strftime(DATE_FMT) - cell_date = ClickableCell(cell_date_name, entry) + cell_date = MainPage_ClickableCell(cell_date_name, entry) cell_date.clicked.connect(self.goto_initial_recording) self.grid.addWidget(cell_date, row, 4) - # self.grid.addWidget(ClickableCell(entry["c2"], entry), row, 1) - - # c3_value = entry.get("c3") - # if c3_value: - # self.grid.addWidget(ClickableCell(c3_value, entry), row, 2) - # else: - # empty_box = QFrame() - # empty_box.setStyleSheet( - # "QFrame { background-color: #f8fafc; border: 2px - # dashed #e2e8f0; border-radius: 8px; }" - # ) - # self.grid.addWidget(empty_box, row, 2) - - # self.grid.addWidget(ClickableCell(entry.get("c4", ""), entry), row, 3) - # self.grid.addWidget(ClickableCell(entry.get("date", ""), entry), row, 4) - self.current_row += 1 # Zähler für den nächsten Eintrag erhöhen def goto_initial_recording( self, - data: backend.FrontpageCompany, + data: types.MainPageEntry, ): - if data.is_company: - self.initrec_company.auto_form.load_data(data.erfassung_id) - self.stack.setCurrentWidget(self.initrec_company) - else: - self.initrec_person.auto_form.load_data(data.erfassung_id) - self.stack.setCurrentWidget(self.initrec_person) + target: QWidget + if data.type is types.InitRecType.COMPANY: + new_state = Page_InitRecCompany_State(session=self.session, un_id=data.rec_id) + self.initrec_company.load_state(new_state) + target = self.initrec_company + # self.stack.setCurrentWidget(self.initrec_company) + elif data.type is types.InitRecType.PERSON: + new_state = Page_InitRecPerson_State(session=self.session, pers_id=data.rec_id) + self.initrec_person.load_state(new_state) + target = self.initrec_person + # self.stack.setCurrentWidget(self.initrec_person) - # 1. Daten an die Detail-Seite übergeben - # self.detail_page.update_content(data) - # 2. Auf die Detail-Seite umblättern - # self.stack.setCurrentWidget(self.detail_page) + self._navigate_to_target(target) def _navigate_to_target( self, @@ -4242,30 +4455,34 @@ class MainWindow(QMainWindow): self._navigate_to_target(self.main_page) def show_new_entry_select(self): - self._navigate_to_target(self.new_entry_select) + self._navigate_to_target(self.new_initrec_select) - def show_page_initrec_company(self, reset: bool): - if reset: - self.initrec_company.reset_form() + def show_page_initrec_company( + self, + new_state: Page_InitRecCompany_State, + ): + self.initrec_company.load_state(new_state) self._navigate_to_target(self.initrec_company) - def show_page_initrec_person(self): - # TODO: similar to company with reset? - self.initrec_person.reset_form() + def show_page_initrec_person( + self, + new_state: Page_InitRecPerson_State, + ): + self.initrec_person.load_state(new_state) self._navigate_to_target(self.initrec_person) def show_page_company_profile( self, - id_: InitRecId, + new_state: Page_CompanyProfile_State, ) -> None: - self.company_profile.load_from_id(id_) + self.company_profile.load_state(new_state) self._navigate_to_target(self.company_profile) def show_page_consulting( self, - req: ConsultingRequest, + new_state: Page_Consulting_State, ) -> None: - self.consulting_page.load_from_request(req) + self.consulting_page.load_from_state(new_state) self._navigate_to_target(self.consulting_page) # --- MENÜ LOGIK --- diff --git a/src/wce_crm/types.py b/src/wce_crm/types.py index 38575a7..b250ea4 100644 --- a/src/wce_crm/types.py +++ b/src/wce_crm/types.py @@ -1,13 +1,84 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeAlias +import dataclasses as dc +import datetime +import enum +from typing import TYPE_CHECKING, TypeAlias, TypedDict if TYPE_CHECKING: import polars as pl UserId: TypeAlias = int -InitRecId: TypeAlias = int +RecId: TypeAlias = int ExtMaId: TypeAlias = int ExtAnId: TypeAlias = int PolarsSchema: TypeAlias = dict[str, type["pl.DataType"]] + + +class InitRecType(enum.IntEnum): + COMPANY = enum.auto() + PERSON = enum.auto() + + +# // database interaction data structures +# ** externals +class CompanyInfo(TypedDict): + ma_id: str + wce_id: str + ma_unternehmensname: str + ma_branche: str + ma_strasse: str + ma_hausnummer: str + ma_plz: str + ma_ort: str + ma_plz_postfach: str + ma_postfach: str + ma_website: str + ma_mail: str + ma_telefonnummer: str + ma_faxnummer: str + ma_ersteintrag_datum: str + ma_aktualisierung_datum: str + ma_aktualisierung_nutzer: str + ma_sollprozess: str + ma_auslaendische_mitarbeiter: str + ma_quelle_information: str + ma_bemerkung: str + ma_kontakt: str + ma_schlagworte: str + ma_archiviert: str + + +class ContactPersonInfo(TypedDict): + an_id: str + ma_id: str + wce_id: str + st_id: str + an_sachgebiet: str + an_anrede: str + an_titel: str + an_nachname: str + an_vorname: str + an_position: str + an_mail: str + an_festnetz: str + an_mobil: str + an_faxnummer: str + an_hauptansprechpartner: str + an_anrede_anschrift: str + an_bemerkung: str + an_aktualisierung_datum: str + an_aktualisierung_nutzer: str + an_letztes_kontaktdatum: str + an_ersteintrag_datum: str + an_archiviert: str + + +# ** internals +@dc.dataclass(slots=True) +class MainPageEntry: + rec_id: RecId + display_name: str + Metadaten_aktualisierung: datetime.datetime + type: InitRecType