continue work on new data model

This commit is contained in:
2026-07-24 17:10:17 +02:00
parent 868db09d94
commit acfc4a824a
9 changed files with 1054 additions and 192 deletions
+295 -95
View File
@@ -85,17 +85,16 @@ from wce_crm.backend import backend
from wce_crm.constants import TIMEZONE_CEST
from wce_crm.data_models import (
COLUMN_SEP,
INITREC_CONFIGS,
Beratungsgespraech_Einzelgespraech,
Beratungsgespraech_Vorgang,
Grunderfassung_Personen,
Grunderfassung_Unternehmen,
InitRec,
Page_CompanyProfile_State,
Page_Consulting_ConsultingSession_State,
Page_Consulting_Linking_State,
Page_Consulting_State,
Page_Consulting_Table_State,
Page_InitRec_Form_Config,
Page_InitRec_Form_Data,
Page_InitRec_Form_State,
Page_InitRec_State,
Page_MainPage_State,
@@ -105,8 +104,6 @@ from wce_crm.data_models import (
translate_pydantic_errors,
)
from wce_crm.form_defs import (
INITREC_COMP,
INITREC_PERSON,
FormField,
FormFieldType,
)
@@ -121,7 +118,8 @@ from wce_crm.logging import (
from wce_crm.types import (
CompanyProfileConsultationEntry,
ConsultingType,
NewEntityIds,
EntityIds,
EntityType,
RecId,
RecordingType,
)
@@ -1547,7 +1545,7 @@ class InitRecForm(QWidget):
# self.save_btn.setText(self.edit_buttons.save_btn_txt_enabled)
def relevant_ids_set(self) -> bool:
return self.STATE.ids.valid(self.STATE.recording_type)
return self.STATE.ids.valid(self.STATE.ent_type)
# def _activate_delete(self) -> None:
# if self.STATE.ids.valid():
@@ -1613,12 +1611,23 @@ class InitRecForm(QWidget):
# pformat(self.STATE.form_data),
# )
def _load_state_form_data(self) -> None:
logger_initrecform.info("[InitRecForm] Load data method...")
logger_initrecform.debug(
"[InitRecForm] Loaded data dict:\n%s Passing to Pydantic...",
pformat(self.STATE.form_data),
)
# TODO error catching?
model = self.cfg.pydantic_model(**self.STATE.form_data)
logger_initrecform.debug("[InitRecForm] Loaded to Pydantic.")
logger_initrecform.debug("[InitRecForm] Convert to GUI structure...")
form_data_gui = model.to_gui()
# TODO include set data in this method?
self._set_data(form_data_gui)
def reset_form(self) -> None:
reset_form(self.widget_registry)
# self.STATE.ids.set_id(None, self.STATE.ent_type)
self.STATE.ids.update(NewEntityIds())
# TODO remove
# self.STATE.rec_id = None
self.STATE.ids.update(EntityIds())
self.STATE.form_data.clear()
self.STATE.geloescht = False
@@ -1658,7 +1667,7 @@ class InitRecForm(QWidget):
# !! loading from IDs is only performed on top-level modules aka pages
if section is None or section == "form":
if not self.STATE.geloescht:
self.load_from_ids(self.STATE.ids)
self._load_state_form_data()
else:
self.reset_form()
@@ -1678,6 +1687,10 @@ class InitRecForm(QWidget):
# TODO change usage of user ID (needs changes in model and backend)
if self.SESSION_USER_KEY in form_data:
form_data[self.SESSION_USER_KEY] = self.STATE.session.user_name
for form_key, id_key in self.STATE.cfg.id_mapping.items():
self.STATE.ids.set_by_name(id_key, form_data[form_key])
self.STATE.form_data = form_data
def validate(self) -> list[str]:
@@ -1695,18 +1708,18 @@ class InitRecForm(QWidget):
return self.STATE
def _load_from_pydantic(
self,
data: InitRec,
) -> None:
# ent_type = self.STATE.recording_type
# self.STATE.ids.set_id(data.ids.get_id(ent_type), ent_type)
self.STATE.ids.update(data.ids)
# self.STATE.rec_id = data.rec_id
self.STATE.form_data.clear()
self.STATE.geloescht = True if data.geloescht else False
# def _load_from_pydantic(
# self,
# data: InitRec,
# ) -> None:
# # ent_type = self.STATE.recording_type
# # self.STATE.ids.set_id(data.ids.get_id(ent_type), ent_type)
# self.STATE.ids.update(data.ids)
# # self.STATE.rec_id = data.rec_id
# self.STATE.form_data.clear()
# self.STATE.geloescht = True if data.geloescht else False
self._sync_state_to_GUI(None) # TODO change to correct loading
# self._sync_state_to_GUI(None) # TODO change to correct loading
# !! must be part of the managing page
def save_data(self) -> None:
@@ -2979,7 +2992,8 @@ class Page_NewEntry(QWidget):
def _request_new_initrec(self) -> None:
req_state = Page_InitRec_State(
session=self.STATE.session,
record_type=RecordingType.COMPANY,
recording_type=RecordingType.WITH_COMPANY,
form_data=Page_InitRec_Form_Data(metadata={}, person={}, company={}),
locked=False,
)
logger_gui.debug("[Page -- NewInitRec] State to call: %s", req_state)
@@ -3009,29 +3023,29 @@ class Page_NewEntry(QWidget):
pass
CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[Page_InitRec_Form_Config] = Page_InitRec_Form_Config(
pydantic_model=Grunderfassung_Unternehmen,
# to_db=backend.initrec_company_to_db, # TODO remove
# from_db=backend.initrec_company_from_db, # TODO remove
ignored_keys=(
"Metadaten_erstellung",
"Metadaten_aktualisierung",
),
form_fields=INITREC_COMP,
add_buttons=False,
)
# CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[Page_InitRec_Form_Config] = Page_InitRec_Form_Config(
# pydantic_model=Grunderfassung_Unternehmen,
# # to_db=backend.initrec_company_to_db, # TODO remove
# # from_db=backend.initrec_company_from_db, # TODO remove
# ignored_keys=(
# "Metadaten_erstellung",
# "Metadaten_aktualisierung",
# ),
# form_fields=INITREC_COMP,
# add_buttons=False,
# )
CONFIG_GRUNDERFASSUNG_PERSONEN: Final[Page_InitRec_Form_Config] = Page_InitRec_Form_Config(
pydantic_model=Grunderfassung_Personen,
# to_db=backend.initrec_person_to_db, # TODO remove
# from_db=backend.initrec_person_from_db, # TODO remove
ignored_keys=(
"Metadaten_erstellung",
"Metadaten_aktualisierung",
),
form_fields=INITREC_PERSON,
add_buttons=False,
)
# CONFIG_GRUNDERFASSUNG_PERSONEN: Final[Page_InitRec_Form_Config] = Page_InitRec_Form_Config(
# pydantic_model=Grunderfassung_Personen,
# # to_db=backend.initrec_person_to_db, # TODO remove
# # from_db=backend.initrec_person_from_db, # TODO remove
# ignored_keys=(
# "Metadaten_erstellung",
# "Metadaten_aktualisierung",
# ),
# form_fields=INITREC_PERSON,
# add_buttons=False,
# )
CUSTOM_WIDGETS: Final[dict[str, type[CustomFormWidget]]] = {
"grunderfassung_unternehmen_suche": Grunderfassung_SuchWidget,
@@ -3128,12 +3142,12 @@ class Page_InitRec(QWidget):
back_btn_step.setMinimumWidth(200)
back_btn_step.setMaximumWidth(200)
if self.STATE.record_type is RecordingType.COMPANY:
if self.STATE.recording_type is RecordingType.WITH_COMPANY:
title = QLabel("Grunderfassung Unternehmen")
elif self.STATE.record_type is RecordingType.PERSON:
elif self.STATE.recording_type is RecordingType.WITHOUT_COMPANY:
title = QLabel("Grunderfassung Individualperson")
else:
raise RuntimeError(f"Unspecified recording type {self.STATE.record_type}")
raise RuntimeError(f"Unspecified recording type {self.STATE.recording_type}")
title.setStyleSheet("font-size: 20px; font-weight: bold;")
header_layout.setSpacing(5)
@@ -3142,6 +3156,64 @@ class Page_InitRec(QWidget):
header_layout.addWidget(title)
vert_layout.addWidget(header_container)
if DEBUG:
separator1 = QFrame()
separator1.setFrameShape(QFrame.Shape.HLine)
separator1.setFrameShadow(QFrame.Shadow.Sunken)
header_layout.addWidget(separator1)
btn_gui2state = QPushButton("Sync GUI to state")
btn_gui2state.clicked.connect(self._sync_GUI_to_state)
btn_gui2state.setFixedHeight(35)
btn_gui2state.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
)
header_layout.addWidget(btn_gui2state)
btn_state2gui = QPushButton("Sync state to GUI")
btn_state2gui.clicked.connect(self._sync_state_to_GUI)
btn_state2gui.setFixedHeight(35)
btn_state2gui.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
)
header_layout.addWidget(btn_state2gui)
# self.test_button = QPushButton("Initialisiere Laden")
# self.test_button.clicked.connect(self._load_from_id)
# self.test_button.setFixedHeight(35)
# self.test_button.setSizePolicy(
# QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
# )
# header_layout.addWidget(self.test_button)
# button_get = QPushButton("GET DATA")
# button_get.setFixedHeight(35)
# button_get.clicked.connect(self._get_form_data)
# header_layout.addWidget(button_get)
# button_set = QPushButton("SET DATA")
# button_set.setFixedHeight(35)
# button_set.clicked.connect(self._set_form_data)
# header_layout.addWidget(button_set)
# id_field_layout = QHBoxLayout()
# id_field_layout.setContentsMargins(0, 0, 0, 0)
# id_field_layout.setSpacing(5)
# id_field_label = QLabel("ID Datenbank:")
# self.id_field_input = QLineEdit()
# id_field_layout.addWidget(id_field_label)
# id_field_layout.addWidget(self.id_field_input)
# header_layout.addLayout(id_field_layout)
# button_db_index = QPushButton("Setze DB Index")
# button_db_index.setFixedHeight(35)
# button_db_index.clicked.connect(self._set_db_index)
# header_layout.addWidget(button_db_index)
separator2 = QFrame()
separator2.setFrameShape(QFrame.Shape.HLine)
separator2.setFrameShadow(QFrame.Shadow.Sunken)
header_layout.addWidget(separator2)
header_layout.addSpacing(5)
# --- BANNER ---
self.info_banners_container = QWidget()
self.info_banners: list[InfoBanner] = []
@@ -3183,35 +3255,29 @@ class Page_InitRec(QWidget):
vert_layout.addWidget(scroll_area)
container_layout = QVBoxLayout(container)
container_layout.setContentsMargins(0, 0, 0, 0)
self.container_layout = QVBoxLayout(container)
self.container_layout.setContentsMargins(0, 0, 0, 0)
# --- AUTO FORM LAYOUT 1 ---
# --- AUTO FORM LAYOUT ---
# now we try to render the layout within the collapsable box
container_layout.addSpacing(20)
self.container_layout.addSpacing(20)
self.initrec_form_company: InitRecForm | None = None
if self.STATE.record_type is RecordingType.COMPANY:
# add company
pass
# ** person
self.initrec_form_person_box = CollapsibleBox("Angaben zur Person")
ent_type = EntityType.PERSON
self.initrec_form_person = InitRecForm(self.STATE.initrec_form_states[ent_type])
assert self.STATE.initrec_form_states[ent_type] is self.initrec_form_person.STATE
box1 = CollapsibleBox("Angaben zur Person")
initrec_form_state_person = Page_InitRec_Form_State(
session=self.STATE.session,
cfg=CONFIG_GRUNDERFASSUNG_PERSONEN,
ids=self.STATE.ids,
recording_type=RecordingType.PERSON,
locked=False,
)
self.initrec_form_person = InitRecForm(initrec_form_state_person)
self.STATE.child_modules.append(self.initrec_form_person)
box1.setContentWidget(self.initrec_form_person)
container_layout.addWidget(box1)
self.initrec_form_person_box.setContentWidget(self.initrec_form_person)
self.container_layout.addWidget(self.initrec_form_person_box)
# container_layout.addSpacing(15)
self.initrec_form_person.update_triggered.connect(
lambda: self.update_triggered.emit()
)
self.initrec_form_person.update_triggered.connect(self._auto_form_updated)
# TODO trigger needed?
# self.initrec_form_person.update_triggered.connect(
# lambda: self.update_triggered.emit()
# )
# self.initrec_form_person.update_triggered.connect(self._auto_form_updated)
# --- CUSTOM LOGIC ---
# ** 'Bundesland' only if 'Inland' selected in 'Stammdaten'
@@ -3233,7 +3299,68 @@ class Page_InitRec(QWidget):
self.selection_county.setProperty("styleClass", "stempel")
self.selection_county.setEnabled(False)
container_layout.addStretch()
# ** company
ent_type = EntityType.COMPANY
self.initrec_form_company_box = CollapsibleBox("Angaben zum Unternehmen")
self.initrec_form_company = InitRecForm(self.STATE.initrec_form_states[ent_type])
self.STATE.child_modules.append(self.initrec_form_company)
assert self.STATE.initrec_form_states[ent_type] is self.initrec_form_company.STATE
target_idx = self.container_layout.indexOf(self.initrec_form_person_box) - 1
self.initrec_form_company_box.setContentWidget(self.initrec_form_company)
self.container_layout.insertWidget(target_idx, self.initrec_form_company_box)
# container_layout.addSpacing(15)
# TODO trigger needed?
# self.initrec_form_person.update_triggered.connect(
# lambda: self.update_triggered.emit()
# )
# self.initrec_form_person.update_triggered.connect(self._auto_form_updated)
# --- CUSTOM LOGIC ---
# ** fill 'Kontaktperson -> Namen Unternehmen'
search_res = search_widgets_by_key(
self.initrec_form_company.widget_registry, "Partnersuche"
)
assert len(search_res) == 1
search_widget = cast(Grunderfassung_SuchWidget, search_res[0]["widget"])
self.search_widget_trigger = cast(
QLineEdit, search_widget.company_widgets["ma_unternehmensname"]
)
self.container_layout.addStretch()
self._update_layout()
def _update_layout(self) -> None:
if self.STATE.recording_type is RecordingType.WITH_COMPANY:
# add company
self.initrec_form_company_box.setVisible(True)
else:
self.initrec_form_company_box.setVisible(False)
# elif self.initrec_form_company_box is not None:
# self.container_layout.removeWidget(self.initrec_form_company_box)
# # ** state
# assert self.initrec_form_company
# self.STATE.child_modules.remove(self.initrec_form_company)
# del self.STATE.initrec_form_states[EntityType.COMPANY]
# self.initrec_form_company_box = None
# self.initrec_form_company = None
# def _set_new_initrec_state(
# self,
# ent_type: EntityType,
# ) -> None:
# initrec_form_state = Page_InitRec_Form_State(
# session=self.STATE.session,
# cfg=INITREC_CONFIGS[ent_type],
# ids=self.STATE.ids,
# ent_type=ent_type,
# locked=False,
# )
# self.STATE.initrec_form_states[ent_type] = initrec_form_state
# ** custom logic
def _custom_county_selection(self, idx: int) -> None:
@@ -3328,18 +3455,28 @@ class Page_InitRec(QWidget):
# TODO staes
for form in self.STATE.child_modules:
form.reset_form()
self.STATE.ids = NewEntityIds()
self.STATE.ids = EntityIds()
self.STATE.geloescht = False
def _load_database(
self,
ids: NewEntityIds,
ids: EntityIds,
) -> None:
logger_initrecform.info("[Page -- InitRec] Load data method...")
logger_initrecform.debug("[Page -- InitRec] Entity IDs: %s", ids)
self._reset()
# TODO add loading logic
initrec: InitRec = ...
# TODO add loading logic with backend call
# !! just a dummy
initrec: InitRec = InitRec(
ids=ids,
recording_type=RecordingType.WITHOUT_COMPANY,
geloescht=None,
form_data=Page_InitRec_Form_Data(
metadata={},
person={},
company={},
),
)
self._load_from_pydantic(initrec, sync_to_GUI=False)
@@ -3349,7 +3486,23 @@ class Page_InitRec(QWidget):
update_only: bool = False,
sync_to_GUI: bool = True,
) -> None:
logger_initrecform.info("[Page -- InitRec] Load from Pydantic model...")
# TODO add logic to update internal state
self.STATE.recording_type = data.recording_type
self.STATE.form_data = data.form_data
self.STATE.geloescht = True if data.geloescht else False
# set form data to correct initrec form states
for ent_type, state in self.STATE.initrec_form_states.items():
match ent_type:
case EntityType.COMPANY:
state.form_data = self.STATE.form_data.company
# self.initrec_form_company.load_state(state)
case EntityType.PERSON:
state.form_data = self.STATE.form_data.person
# self.initrec_form_person.load_state(state)
case _:
raise RuntimeError(f"Unspecified entity type {ent_type}")
if update_only:
...
@@ -3368,19 +3521,20 @@ class Page_InitRec(QWidget):
)
if section is None or section == "content":
self._update_layout()
if not self.STATE.geloescht:
self._load_database(self.STATE.ids)
else:
self._reset()
initrec_form_state_person = Page_InitRec_Form_State(
session=self.STATE.session,
cfg=CONFIG_GRUNDERFASSUNG_PERSONEN,
ids=self.STATE.ids,
recording_type=RecordingType.PERSON,
locked=False,
)
self.initrec_form_person.load_state(initrec_form_state_person)
# initrec_form_state_person = Page_InitRec_Form_State(
# session=self.STATE.session,
# cfg=CONFIG_GRUNDERFASSUNG_PERSONEN,
# ids=self.STATE.ids,
# ent_type=RecordingType.WITHOUT_COMPANY,
# locked=False,
# )
# self.initrec_form_person.load_state(initrec_form_state_person)
if section is None or section == "banners":
self._clear_info_banners()
@@ -3401,22 +3555,65 @@ class Page_InitRec(QWidget):
form.unlock()
def _sync_GUI_to_state(self) -> None:
logger_page_initrec.debug("[Page -- InitRec] Syncing GUI to state...")
logger_page_initrec.debug(
"[Page -- InitRec] Syncing GUI to state: Child modules...\n%s",
pformat(self.STATE.child_modules),
)
logger_page_initrec.debug(
"[Page -- InitRec] Syncing GUI to state: Existing form states keys...\n%s",
pformat(self.STATE.initrec_form_states.keys()),
)
for form in self.STATE.child_modules:
# ?? is this the same reference to use a similar pattern as
# ?? the consulting page (initialise once with the same state
# ?? as reference which are then automatically updated)
# !! check if this is working as expected
# new entries' states are linked to this instance by the page state
form_state = form.get_state()
self.STATE.ids.update(form_state.ids, ignore_none=True)
self.STATE.form_data[form_state.recording_type] = form_state.form_data
self.STATE.pydantic_models[form_state.recording_type] = (
form_state.cfg.pydantic_model
logger_page_initrec.debug(
"[Page -- InitRec] Syncing GUI to state: Form state ent_type %s",
form_state.ent_type,
)
logger_page_initrec.debug(
"[Page -- InitRec] Syncing GUI to state: identity of form states: %s",
(self.STATE.initrec_form_states[form_state.ent_type] is form_state),
)
assert self.STATE.initrec_form_states[form_state.ent_type] is form_state, (
"passing by reference not working"
)
self.STATE.ids.update(form_state.ids, ignore_none=True)
# self.STATE.form_data[form_state.ent_type] = form_state.form_data
match form_state.ent_type:
case EntityType.COMPANY:
self.STATE.form_data.company = form_state.form_data
case EntityType.PERSON:
self.STATE.form_data.person = form_state.form_data
case _:
raise RuntimeError(f"Unspecified entity type {form_state.ent_type}")
logger_page_initrec.debug(
"[Page -- InitRec] Syncing GUI to state: IDs after...\n%s",
pformat(self.STATE.ids),
)
logger_page_initrec.debug(
"[Page -- InitRec] Syncing GUI to state: Form data after...\n%s",
pformat(self.STATE.form_data),
)
def load_state(
self,
new_state: Page_InitRec_State,
) -> None:
set_page_state(self.STATE, new_state)
set_page_state(
self.STATE,
new_state,
ignore_attr={"form_data", "initrec_form_states"},
)
self._sync_state_to_GUI(None)
def get_state(self) -> Page_InitRec_State:
@@ -3444,6 +3641,8 @@ class Page_InitRec(QWidget):
def save_data(self) -> None:
logger_page_initrec.debug("[Page -- InitRec] Save button clicked...")
return
if self.STATE.ids.empty() and self.STATE.geloescht:
logger_page_initrec.debug(
"[Page -- InitRec] This was never saved in the database "
@@ -3460,7 +3659,7 @@ class Page_InitRec(QWidget):
to_db = InitRec(
ids=self.STATE.ids,
geloescht=deleted_datetime,
db_data={},
form_data={},
)
returned = self.cfg.to_db(to_db)
self._load_from_pydantic(returned)
@@ -3520,7 +3719,7 @@ class Page_InitRec(QWidget):
ids=self.STATE.ids,
# rec_id=self.STATE.rec_id,
geloescht=deleted_datetime,
db_data=db_data,
form_data=db_data,
)
returned = self.cfg.to_db(to_db)
@@ -5939,8 +6138,9 @@ class MainWindow(QMainWindow):
# SITE: 'Grunderfassung'
newinitrec_state = Page_InitRec_State(
session=session,
ids=NewEntityIds(),
record_type=RecordingType.COMPANY,
ids=EntityIds(),
recording_type=RecordingType.WITH_COMPANY,
form_data=Page_InitRec_Form_Data(metadata={}, person={}, company={}),
locked=False,
)
self.newinitrec = Page_InitRec(newinitrec_state)
@@ -5973,7 +6173,7 @@ class MainWindow(QMainWindow):
# self.stack.addWidget(self.initrec_person)
# SITE: 'Unternehmensprofil'
comp_profile_state = Page_CompanyProfile_State(
session=self.STATE.session, ids=NewEntityIds()
session=self.STATE.session, ids=EntityIds()
)
self.company_profile = Page_CompanyProfile(comp_profile_state)
self.company_profile.back_main_requested.connect(self.show_main_page)