From c1ba3cf6e709ab0ddb706b891fa8ed5a0f322ceb Mon Sep 17 00:00:00 2001 From: foefl Date: Mon, 20 Jul 2026 15:48:36 +0200 Subject: [PATCH] added new data model principle to auto forms --- src/wce_crm/backend/backend.py | 25 +++---- src/wce_crm/data_models.py | 20 +++--- src/wce_crm/gui.py | 118 +++++++++++++++++++++------------ 3 files changed, 97 insertions(+), 66 deletions(-) diff --git a/src/wce_crm/backend/backend.py b/src/wce_crm/backend/backend.py index eeb8b30..9b3ca5d 100644 --- a/src/wce_crm/backend/backend.py +++ b/src/wce_crm/backend/backend.py @@ -14,7 +14,7 @@ from wce_crm.constants import TIMEZONE_CEST from wce_crm.data_models import ( Beratungsgespraech_Einzelgespraech, Beratungsgespraech_Vorgang, - Initrec_FromDb, + Initrec, ) from wce_crm.logging import logger_back as logger from wce_crm.types import ( @@ -28,7 +28,6 @@ from wce_crm.types import ( ) if TYPE_CHECKING: - from wce_crm.data_models import AutoForm_State, Initrec from wce_crm.types import ConsId, ExtAnId, ExtMaId, RecId @@ -153,7 +152,7 @@ def initrec_company_to_db( ) -> Initrec: logger.debug("[AutoForm -- backend] Call database saving routine...") - dump_data = copy.deepcopy(auto_form_data.form_data) + dump_data = copy.deepcopy(auto_form_data.db_data) dump_data["geloescht"] = auto_form_data.geloescht with db.ENGINE.begin() as conn: @@ -174,7 +173,7 @@ def initrec_company_to_db( data_from_db = from_db[0] auto_form_data.rec_id = data_from_db["un_id"] auto_form_data.geloescht = data_from_db["geloescht"] - auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[ + auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[ "Metadaten_aktualisierung" ] @@ -183,7 +182,7 @@ def initrec_company_to_db( else: logger.debug("[AutoForm -- backend] Update...") stmt = ( - db.beratung_vorgang.update() + db.grunderfassung_unternehmen.update() .where(db.grunderfassung_unternehmen.c.un_id == auto_form_data.rec_id) .returning( db.grunderfassung_unternehmen.c.Metadaten_aktualisierung, @@ -196,8 +195,7 @@ def initrec_company_to_db( assert len(from_db) == 1, "expected excatly one returned row" data_from_db = from_db[0] - auto_form_data.geloescht = data_from_db["geloescht"] - auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[ + auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[ "Metadaten_aktualisierung" ] @@ -229,7 +227,7 @@ def initrec_company_from_db( return Initrec( rec_id=id_, geloescht=geloescht, - form_data=data_from_db, + db_data=data_from_db, ) @@ -238,7 +236,7 @@ def initrec_person_to_db( ) -> Initrec: logger.debug("[AutoForm -- backend] Call database saving routine...") - dump_data = copy.deepcopy(auto_form_data.form_data) + dump_data = copy.deepcopy(auto_form_data.db_data) dump_data["geloescht"] = auto_form_data.geloescht with db.ENGINE.begin() as conn: @@ -259,7 +257,7 @@ def initrec_person_to_db( data_from_db = from_db[0] auto_form_data.rec_id = data_from_db["un_id"] auto_form_data.geloescht = data_from_db["geloescht"] - auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[ + auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[ "Metadaten_aktualisierung" ] @@ -268,7 +266,7 @@ def initrec_person_to_db( else: logger.debug("[AutoForm -- backend] Update...") stmt = ( - db.beratung_vorgang.update() + db.grunderfassung_personen.update() .where(db.grunderfassung_personen.c.pers_id == auto_form_data.rec_id) .returning( db.grunderfassung_personen.c.Metadaten_aktualisierung, @@ -281,8 +279,7 @@ def initrec_person_to_db( assert len(from_db) == 1, "expected excatly one returned row" data_from_db = from_db[0] - auto_form_data.geloescht = data_from_db["geloescht"] - auto_form_data.form_data["Metadaten_aktualisierung"] = data_from_db[ + auto_form_data.db_data["Metadaten_aktualisierung"] = data_from_db[ "Metadaten_aktualisierung" ] @@ -314,7 +311,7 @@ def initrec_person_from_db( return Initrec( rec_id=id_, geloescht=geloescht, - form_data=data_from_db, + db_data=data_from_db, ) diff --git a/src/wce_crm/data_models.py b/src/wce_crm/data_models.py index 2894cf8..d47e5e4 100644 --- a/src/wce_crm/data_models.py +++ b/src/wce_crm/data_models.py @@ -225,10 +225,10 @@ class AutoFormConfig: model: type[FlatBaseModel] to_db: AutoFormToDb from_db: AutoFormFromDb - data_insert: AutoFormInsert - data_update: AutoFormUpdate - data_get: AutoFormGet - data_delete: AutoFormDelete + # data_insert: AutoFormInsert + # data_update: AutoFormUpdate + # data_get: AutoFormGet + # data_delete: AutoFormDelete form_fields: Sequence[FormField] ignored_keys: Iterable[str] = tuple() add_buttons: bool = True @@ -412,17 +412,17 @@ class FlatBaseModel(BaseModel): # ** InitRec -class Initrec_FromDb(BaseModel): - rec_id: RecId - geloescht: AwareDatetime | None - Metadaten_aktualisierung: AwareDatetime - form_data: dict[str, Any] +# class Initrec_FromDb(BaseModel): +# rec_id: RecId +# geloescht: AwareDatetime | None +# Metadaten_aktualisierung: AwareDatetime +# form_data: dict[str, Any] class Initrec(BaseModel): rec_id: RecId | None geloescht: AwareDatetime | None = None - form_data: dict[str, Any] + db_data: dict[str, Any] class Grunderfassung_Unternehmen(FlatBaseModel): diff --git a/src/wce_crm/gui.py b/src/wce_crm/gui.py index af92cce..8dd1972 100644 --- a/src/wce_crm/gui.py +++ b/src/wce_crm/gui.py @@ -1532,9 +1532,17 @@ class AutoForm(QWidget): 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) + confirm = get_user_confirmation( + self, + "Löschen bestätigen", + "Soll der Eintrag wirklich gelöscht werden?", + ) + if not confirm: + return + + self.STATE.geloescht = True + self.save_data() self.update_triggered.emit() - self.reset_form() def _load_from_id( self, @@ -1544,28 +1552,26 @@ class AutoForm(QWidget): return logger_auto_form.info("[Auto-Form] Load data method...") + logger_auto_form.debug("[Auto-Form] Lookup ID: %s", id_) self.reset_form() - logger_auto_form.debug("[Auto-Form] Lookup ID: %s", id_) - - if id_ is not None: - logger_auto_form.debug("Load from DB:") - returned = self.cfg.from_db(id_) - # loaded_data = self.cfg.data_get(lookup_id) - # TODO add path to avoid unnecessary loading (where the same ID is loaded - # twice in a row) -- use an internal marker (not state-related) - else: + if id_ is None: logger_auto_form.debug("[Auto-Form] Loading: Lookup ID NULL. Do nothing!") return + logger_auto_form.debug("Load from DB:") + returned = self.cfg.from_db(id_) + + # TODO add path to avoid unnecessary loading (where the same ID is loaded + # twice in a row) -- use an internal marker (not state-related) # logger_auto_form.debug( # "[Auto-Form] Loaded data dict:\n%s Passing to Pydantic...", pformat(loaded_data) # ) logger_auto_form.debug( "[Auto-Form] Loaded data dict:\n%s Passing to Pydantic...", - pformat(returned.form_data), + pformat(returned.db_data), ) - model = self.cfg.model(**returned.form_data) + model = self.cfg.model(**returned.db_data) logger_auto_form.debug("[Auto-Form] Loaded to Pydantic.") logger_auto_form.debug("[Auto-Form] Convert to GUI structure...") form_data = model.to_gui() @@ -1573,13 +1579,17 @@ class AutoForm(QWidget): logger_auto_form.debug("[Auto-Form] Form data:\n%s", pformat(form_data)) self._set_form_data(form_data) self.STATE.rec_id = id_ - self._activate_delete() + self.STATE.form_data = self._get_form_data() + logger_auto_form.debug( + "####### model dump vs get form data:\n%s\n\n%s", + pformat(model.model_dump(mode="python")), + pformat(self.STATE.form_data), + ) def reset_form(self) -> None: reset_form(self.widget_registry) self.STATE.rec_id = None self.STATE.form_data = None - self._activate_delete() def lock_form(self) -> None: self.STATE.locked = True @@ -1614,17 +1624,21 @@ class AutoForm(QWidget): section: Literal["form", "locking"] | None, ) -> None: if section is None or section == "form": - self._load_from_id(self.STATE.rec_id) + if not self.STATE.geloescht: + self._load_from_id(self.STATE.rec_id) + else: + self.reset_form() if section is None or section == "locking": if self.STATE.locked: if self.edit_buttons is not None: - self.edit_buttons.set_modify_state(False) + self.edit_buttons.set_modify_state(False, set_delete_btn=True) lock_form(self.widget_registry) else: if self.edit_buttons is not None: - self.edit_buttons.set_modify_state(True) + self.edit_buttons.set_modify_state(True, set_delete_btn=False) unlock_form(self.widget_registry) + self._activate_delete() def _sync_GUI_to_state(self) -> None: form_data = self._get_form_data() @@ -1647,14 +1661,46 @@ class AutoForm(QWidget): return self.STATE - def _load_from_pydantic(self, data: Initrec) -> None: + def _load_from_pydantic( + self, + data: Initrec, + ) -> None: self.STATE.rec_id = data.rec_id - self.STATE.form_data = data.form_data + self.STATE.form_data = None self.STATE.geloescht = True if data.geloescht else False self._sync_state_to_GUI(None) # TODO change to correct loading def save_data(self) -> None: + if self.STATE.rec_id is None and self.STATE.geloescht: + logger_auto_form.debug( + "[Auto-Form] This was never saved in the database " + "and is marked for deletion - ignore." + ) + return + elif self.STATE.rec_id is not None and self.STATE.geloescht: + logger_auto_form.debug( + "[Auto-Form] This was saved in the database " + "and is marked for deletion - fast path." + ) + deleted_datetime = datetime.datetime.now(datetime.UTC) + assert self.STATE.form_data, "cannot delete entry with uninitialised form data" + to_db = Initrec( + rec_id=self.STATE.rec_id, + geloescht=deleted_datetime, + db_data={}, + ) + returned = self.cfg.to_db(to_db) + self._load_from_pydantic(returned) + msg_box = get_message_box( + QMessageBox.Icon.Information, + "Löschen erfolgreich", + "Der Eintrag wurde erfolgreich gelöscht", + ) + msg_box.exec() + + return + self._disable_save() errors = self.validate() @@ -1699,23 +1745,12 @@ class AutoForm(QWidget): to_db = Initrec( rec_id=self.STATE.rec_id, geloescht=deleted_datetime, - form_data=db_data, + db_data=db_data, ) returned = self.cfg.to_db(to_db) self._load_from_pydantic(returned) - - # if self.STATE.rec_id is None: - # logger_auto_form.debug("[Auto-Form] Insert triggered...") - # init_rec_id = self.cfg.data_insert(db_data) - # assert isinstance(init_rec_id, int) - # self.STATE.rec_id = init_rec_id - # else: - # logger_auto_form.debug("[Auto-Form] Update triggered...") - # self.cfg.data_update(self.STATE.rec_id, db_data) - self.update_triggered.emit() - self._activate_delete() except Exception as err: msg_box = get_message_box( QMessageBox.Icon.Warning, @@ -2790,6 +2825,11 @@ class MainPage_ClickableCell(ClickableCell): super().__init__(text=text) self.data_record = data_record + logger_gui.debug( + "Initialised ClickableCell on MainPage with data record:\n%s", + pformat(self.data_record), + ) + def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.clicked.emit(self.data_record) @@ -2903,10 +2943,6 @@ CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig( model=Grunderfassung_Unternehmen, to_db=backend.initrec_company_to_db, from_db=backend.initrec_company_from_db, - data_insert=backend.initrec_company_insert_initial_recording, - data_update=backend.initrec_company_update_initial_recording, - data_get=backend.initrec_company_get_initial_recording, - data_delete=backend.initrec_company_delete_initial_recording, ignored_keys=( "Metadaten_erstellung", "Metadaten_aktualisierung", @@ -2918,10 +2954,6 @@ CONFIG_GRUNDERFASSUNG_PERSONEN: Final[AutoFormConfig] = AutoFormConfig( model=Grunderfassung_Personen, to_db=backend.initrec_person_to_db, from_db=backend.initrec_person_from_db, - data_insert=backend.initrec_person_insert_initial_recording, - data_update=backend.initrec_person_update_initial_recording, - data_get=backend.initrec_person_get_initial_recording, - data_delete=backend.initrec_person_delete_initial_recording, ignored_keys=( "Metadaten_erstellung", "Metadaten_aktualisierung", @@ -3100,6 +3132,7 @@ class Page_InitRecCompany(QWidget): form_data=None, locked=self.STATE.locked, ) + logger_gui.debug(">>>>>>>>>>>>> AutoForm State:\n%s", pformat(auto_form_state)) self.auto_form.load_state(auto_form_state) if section is None or section == "comp_profile": @@ -4490,7 +4523,6 @@ class Page_Consulting(QWidget): "[Consulting Page] Pydantic model:\n%s", consulting_process ) - # !! should be placed in a try block to handle errors res = backend.page_consulting_to_db(consulting_process) if res.status != STATUS_HANDLER.SUCCESS: logger_page_consulting.error( @@ -5294,11 +5326,13 @@ class EditButtons(QWidget): def set_modify_state( self, enable_modification: bool, + set_delete_btn: bool = True, ) -> None: if self.modify_button is not None: self.modify_button.set_modify_state(enable_modification) self.save_btn.setEnabled(enable_modification) - self.delete_btn.setEnabled(enable_modification) + if set_delete_btn: + self.delete_btn.setEnabled(enable_modification) class MainWindow(QMainWindow):