generated from dopt-python/py311
working example for document management
This commit was merged in pull request #6.
This commit is contained in:
+156
-22
@@ -82,17 +82,19 @@ 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.constants import COLUMN_SEP, TIMEZONE_CEST
|
||||
from wce_crm.data_models import (
|
||||
COLUMN_SEP,
|
||||
Beratungsgespraech_Einzelgespraech,
|
||||
Beratungsgespraech_Vorgang,
|
||||
Grunderfassung_Dokument,
|
||||
InitRec,
|
||||
Page_CompanyProfile_State,
|
||||
Page_Consulting_ConsultingSession_State,
|
||||
Page_Consulting_Linking_State,
|
||||
Page_Consulting_State,
|
||||
Page_Consulting_Table_State,
|
||||
Page_InitRec_DocumentSection_Doc_State,
|
||||
Page_InitRec_DocumentSection_State,
|
||||
Page_InitRec_Form_Data,
|
||||
Page_InitRec_Form_State,
|
||||
Page_InitRec_State,
|
||||
@@ -111,6 +113,7 @@ from wce_crm.gui_components.dialogs import (
|
||||
get_user_confirmation,
|
||||
pydantic_validation_error_handling,
|
||||
)
|
||||
from wce_crm.gui_components.initrec_file_selection import DocumentSectionWidget
|
||||
from wce_crm.logging import (
|
||||
logger_custom_fields,
|
||||
logger_get_data,
|
||||
@@ -3083,12 +3086,17 @@ CUSTOM_WIDGETS: Final[dict[str, type[CustomFormWidget]]] = {
|
||||
|
||||
|
||||
class CollapsibleBox(QWidget):
|
||||
def __init__(self, title="", parent=None):
|
||||
def __init__(
|
||||
self,
|
||||
title: str = "",
|
||||
parent: QWidget | None = None,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
# 1. Header-Button mit Pfeil
|
||||
self.toggle_button = QToolButton()
|
||||
self.toggle_button.setText(f" {title}")
|
||||
self.base_text = f" {title}"
|
||||
self.toggle_button.setText(self.base_text)
|
||||
self.toggle_button.setCheckable(True)
|
||||
self.toggle_button.setChecked(False)
|
||||
self.toggle_button.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
||||
@@ -3108,7 +3116,10 @@ class CollapsibleBox(QWidget):
|
||||
main_layout.addWidget(self.toggle_button)
|
||||
main_layout.addWidget(self.content_area)
|
||||
|
||||
def _on_toggle(self, checked: bool):
|
||||
def _on_toggle(
|
||||
self,
|
||||
checked: bool,
|
||||
) -> None:
|
||||
# Pfeilrichtung ändern & Inhalt ein-/ausblenden
|
||||
self.toggle_button.setArrowType(
|
||||
Qt.ArrowType.DownArrow if checked else Qt.ArrowType.RightArrow
|
||||
@@ -3118,18 +3129,25 @@ class CollapsibleBox(QWidget):
|
||||
def setContentLayout(
|
||||
self,
|
||||
layout: QLayout,
|
||||
):
|
||||
) -> None:
|
||||
self.content_area.setLayout(layout)
|
||||
|
||||
def setContentWidget(
|
||||
self,
|
||||
widget: QWidget,
|
||||
):
|
||||
) -> None:
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0) # Keine zusätzlichen Ränder
|
||||
layout.addWidget(widget)
|
||||
self.setContentLayout(layout)
|
||||
|
||||
def set_additional_text(
|
||||
self,
|
||||
text: str,
|
||||
) -> None:
|
||||
new_txt = self.base_text + text
|
||||
self.toggle_button.setText(new_txt)
|
||||
|
||||
def set_open(
|
||||
self,
|
||||
open: bool,
|
||||
@@ -3362,13 +3380,30 @@ class Page_InitRec(QWidget):
|
||||
QLineEdit, search_widget.company_widgets["ma_unternehmensname"]
|
||||
)
|
||||
|
||||
# ** documents
|
||||
self.docs_box = CollapsibleBox("Dokumente")
|
||||
docs_widget_state = Page_InitRec_DocumentSection_State(
|
||||
session=self.STATE.session,
|
||||
docs=self.STATE.docs,
|
||||
locked=self.STATE.locked,
|
||||
geloescht=self.STATE.geloescht,
|
||||
)
|
||||
self.docs_widget = DocumentSectionWidget(docs_widget_state)
|
||||
self.docs_box.setContentWidget(self.docs_widget)
|
||||
self.container_layout.addWidget(self.docs_box)
|
||||
self.docs_widget.doc_count_changed.connect(self._trigger_update_docs_box_title)
|
||||
|
||||
self.container_layout.addStretch()
|
||||
self._update_layout()
|
||||
|
||||
def _debug_print_state(self) -> None:
|
||||
logger_initrecform.info(
|
||||
self._sync_GUI_to_state()
|
||||
logger_page_initrec.info(
|
||||
"[Page -- InitRec] Printing state IDs:\n%s", pformat(self.STATE.ids)
|
||||
)
|
||||
logger_page_initrec.info(
|
||||
"[Page -- InitRec] Printing state Docs:\n%s", pformat(self.STATE.docs)
|
||||
)
|
||||
|
||||
def _request_company_profile(self) -> None:
|
||||
assert self.STATE.ids.un_id is not None, (
|
||||
@@ -3378,9 +3413,16 @@ class Page_InitRec(QWidget):
|
||||
session=self.STATE.session,
|
||||
un_id=self.STATE.ids.un_id,
|
||||
)
|
||||
logger_gui.debug("[Page -- InitRec Company] State to call: %s", req_state)
|
||||
logger_page_initrec.debug("[Page -- InitRec] State to call: %s", req_state)
|
||||
self.company_profile_requested.emit(req_state)
|
||||
|
||||
def _trigger_update_docs_box_title(
|
||||
self,
|
||||
count: int,
|
||||
) -> None:
|
||||
add_txt = f" ({count})"
|
||||
self.docs_box.set_additional_text(add_txt)
|
||||
|
||||
def _update_layout(self) -> None:
|
||||
self.initrec_form_metadata_box.set_open(True)
|
||||
if self.STATE.recording_type is RecordingType.WITH_COMPANY:
|
||||
@@ -3518,6 +3560,7 @@ class Page_InitRec(QWidget):
|
||||
def _reset(self) -> None:
|
||||
for form in self.STATE.child_modules:
|
||||
form.reset_form()
|
||||
self.STATE.docs.clear()
|
||||
self.STATE.ids = EntityIds()
|
||||
self.STATE.geloescht = False
|
||||
|
||||
@@ -3543,7 +3586,6 @@ class Page_InitRec(QWidget):
|
||||
logger_initrecform.debug("[Page -- InitRec] Loading: No ID set. Do nothing!")
|
||||
return
|
||||
|
||||
# TODO add loading logic with backend call
|
||||
initrec = backend.page_initrec_from_db(ids)
|
||||
|
||||
# TODO re-enable later
|
||||
@@ -3612,6 +3654,31 @@ class Page_InitRec(QWidget):
|
||||
case _:
|
||||
raise RuntimeError(f"Unspecified entity type {ent_type}")
|
||||
|
||||
if update_only:
|
||||
relevant_gui_states = [s for s in self.STATE.docs if not s.geloescht]
|
||||
rel_doc_states = [s for s in data.docs if s.geloescht is None]
|
||||
assert len(relevant_gui_states) == len(rel_doc_states)
|
||||
for gui_state, pydantic_state in zip(relevant_gui_states, rel_doc_states):
|
||||
gui_state.id = pydantic_state.id
|
||||
gui_state.speicher_pfad = pydantic_state.speicher_pfad
|
||||
else:
|
||||
self.STATE.docs.clear()
|
||||
for pydantic_state in data.docs:
|
||||
# Deleted sessions are not part of the retrieved data from the backend if this
|
||||
# page is initialised from a fresh backend call to get data. (Updates do
|
||||
# indeed have data which must be filtered -- see above)
|
||||
doc_state = Page_InitRec_DocumentSection_Doc_State(
|
||||
session=self.STATE.session,
|
||||
id=pydantic_state.id,
|
||||
speicher_pfad=pydantic_state.speicher_pfad,
|
||||
dateiname_original=pydantic_state.dateiname_original,
|
||||
datum_hinzugefuegt=pydantic_state.datum_hinzugefuegt,
|
||||
mime_type=pydantic_state.mime_type,
|
||||
anmerkung=pydantic_state.anmerkung,
|
||||
locked=self.STATE.locked,
|
||||
)
|
||||
self.STATE.docs.append(doc_state)
|
||||
|
||||
if sync_to_GUI:
|
||||
self._sync_state_to_GUI(None)
|
||||
|
||||
@@ -3630,6 +3697,14 @@ class Page_InitRec(QWidget):
|
||||
self._reset()
|
||||
self._update_layout()
|
||||
|
||||
doc_widget_state = Page_InitRec_DocumentSection_State(
|
||||
session=self.STATE.session,
|
||||
docs=self.STATE.docs,
|
||||
locked=self.STATE.locked,
|
||||
geloescht=self.STATE.geloescht,
|
||||
)
|
||||
self.docs_widget.load_state(doc_widget_state)
|
||||
|
||||
if section is None or section == "banners":
|
||||
self._clear_info_banners()
|
||||
if self.STATE.ids.pers_id is None:
|
||||
@@ -3642,10 +3717,12 @@ class Page_InitRec(QWidget):
|
||||
if self.STATE.locked:
|
||||
for form in self.STATE.child_modules:
|
||||
form.lock()
|
||||
self.docs_widget.lock()
|
||||
self.edit_buttons.set_modify_state(False, set_delete_btn=True)
|
||||
else:
|
||||
for form in self.STATE.child_modules:
|
||||
form.unlock()
|
||||
self.docs_widget.unlock()
|
||||
self.edit_buttons.set_modify_state(True, set_delete_btn=False)
|
||||
self._activate_delete()
|
||||
|
||||
@@ -3693,6 +3770,9 @@ class Page_InitRec(QWidget):
|
||||
case _:
|
||||
raise RuntimeError(f"Unspecified entity type {form_state.ent_type}")
|
||||
|
||||
docs_widget_state = self.docs_widget.get_state()
|
||||
self.STATE.docs = docs_widget_state.docs
|
||||
|
||||
logger_page_initrec.debug(
|
||||
"[Page -- InitRec] Syncing GUI to state: IDs after...\n%s",
|
||||
pformat(self.STATE.ids),
|
||||
@@ -3729,6 +3809,8 @@ class Page_InitRec(QWidget):
|
||||
|
||||
errors.extend(m.validate())
|
||||
|
||||
errors.extend(self.docs_widget.validate())
|
||||
|
||||
return errors
|
||||
|
||||
def lock(self) -> None:
|
||||
@@ -3763,13 +3845,35 @@ class Page_InitRec(QWidget):
|
||||
"[Page -- InitRec] This was saved in the database "
|
||||
"and is marked for deletion - fast path."
|
||||
)
|
||||
deleted_datetime = datetime.datetime.now(datetime.UTC)
|
||||
ts_deleted = datetime.datetime.now(datetime.UTC)
|
||||
assert self.STATE.form_data, "cannot delete entry with uninitialised form data"
|
||||
|
||||
pydantic_docs: list[Grunderfassung_Dokument] = []
|
||||
for doc_state in self.STATE.docs:
|
||||
if doc_state.id is None:
|
||||
logger_page_initrec.debug(
|
||||
(
|
||||
"[Page -- InitRec] State which was never saved in the database "
|
||||
"and is marked for deletion - ignore."
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
doc_state_dict = dc.asdict(doc_state)
|
||||
del doc_state_dict["geloescht"]
|
||||
doc = Grunderfassung_Dokument(
|
||||
geloescht=ts_deleted,
|
||||
pers_id=self.STATE.ids.pers_id,
|
||||
**doc_state_dict,
|
||||
)
|
||||
pydantic_docs.append(doc)
|
||||
|
||||
data_to_db = InitRec(
|
||||
ids=self.STATE.ids,
|
||||
recording_type=self.STATE.recording_type,
|
||||
geloescht=deleted_datetime,
|
||||
geloescht=ts_deleted,
|
||||
form_data=db_data,
|
||||
docs=pydantic_docs,
|
||||
)
|
||||
returned = backend.page_initrec_to_db(data_to_db)
|
||||
self._load_from_pydantic(returned)
|
||||
@@ -3817,6 +3921,45 @@ class Page_InitRec(QWidget):
|
||||
case _:
|
||||
raise RuntimeError(f"Unspecified entity type {ent_type}")
|
||||
|
||||
ts_deleted: datetime.datetime | None = None
|
||||
if self.STATE.geloescht:
|
||||
ts_deleted = datetime.datetime.now(datetime.UTC)
|
||||
|
||||
pydantic_docs: list[Grunderfassung_Dokument] = []
|
||||
for doc_state in self.STATE.docs:
|
||||
if doc_state.id is None and doc_state.geloescht:
|
||||
logger_page_initrec.debug(
|
||||
(
|
||||
"[Page -- InitRec] State which was never saved in the database "
|
||||
"and is marked for deletion - ignore."
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
ts_deleted_doc: datetime.datetime | None = None
|
||||
if ts_deleted is not None:
|
||||
# if process is deleted, all sessions must be marked as deleted
|
||||
ts_deleted_doc = ts_deleted
|
||||
elif doc_state.geloescht:
|
||||
ts_deleted_doc = datetime.datetime.now(datetime.UTC)
|
||||
|
||||
doc_state_dict = dc.asdict(doc_state)
|
||||
del doc_state_dict["geloescht"]
|
||||
doc = Grunderfassung_Dokument(
|
||||
geloescht=ts_deleted_doc,
|
||||
pers_id=self.STATE.ids.pers_id,
|
||||
**doc_state_dict,
|
||||
)
|
||||
pydantic_docs.append(doc)
|
||||
|
||||
data_to_db = InitRec(
|
||||
ids=self.STATE.ids,
|
||||
recording_type=self.STATE.recording_type,
|
||||
geloescht=ts_deleted,
|
||||
form_data=db_data,
|
||||
docs=pydantic_docs,
|
||||
)
|
||||
|
||||
except ValidationError as e:
|
||||
logger_page_initrec.error(
|
||||
"[Page -- InitRec] Error during Pydantic validation phase:\n%s",
|
||||
@@ -3834,19 +3977,10 @@ class Page_InitRec(QWidget):
|
||||
"[Page -- InitRec] Form data with 'exlude' (must be saved in the database):\n%s",
|
||||
pformat(db_data),
|
||||
)
|
||||
deleted_datetime: datetime.datetime | None = None
|
||||
if self.STATE.geloescht:
|
||||
deleted_datetime = datetime.datetime.now(datetime.UTC)
|
||||
|
||||
try:
|
||||
# TODO change to result pattern
|
||||
data_to_db = InitRec(
|
||||
ids=self.STATE.ids,
|
||||
recording_type=self.STATE.recording_type,
|
||||
geloescht=deleted_datetime,
|
||||
form_data=db_data,
|
||||
)
|
||||
returned = backend.page_initrec_to_db(data_to_db)
|
||||
|
||||
self._load_from_pydantic(returned, update_only=True, sync_to_GUI=False)
|
||||
self.update_triggered.emit()
|
||||
except Exception as err:
|
||||
|
||||
Reference in New Issue
Block a user