From 90e441d719acac1f8554485f4d8c0abe2df5b1de Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 10:43:41 +0200 Subject: [PATCH 01/10] upgrade ``dopt-basics`` for new logging --- pdm.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pdm.lock b/pdm.lock index 46bbfb0..cd1f265 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "lint", "nb", "tests"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:4b3f779c4a10819bdc84e071dbf4016edd7429afeb4697b5ac7e622b3c870d87" +content_hash = "sha256:25812ee6ba42033e1341c1799716828d8582092826c1d0889ea775a0f5c548a2" [[metadata.targets]] requires_python = ">=3.11,<3.14" @@ -975,7 +975,7 @@ files = [ [[package]] name = "dopt-basics" -version = "0.2.5" +version = "0.2.6" requires_python = ">=3.11" summary = "basic cross-project tools for Python-based d-opt projects" groups = ["default"] @@ -983,8 +983,8 @@ dependencies = [ "tzdata>=2025.1", ] files = [ - {file = "dopt_basics-0.2.5-py3-none-any.whl", hash = "sha256:e5a920054463af782ff05526d2ae726d2d3533d7a6419a0f1edeee43b0118ca5"}, - {file = "dopt_basics-0.2.5.tar.gz", hash = "sha256:3f26a1c2d9557c41ec9b6452ee0e2cfd256e64160386708a2f69b2e36b403ad3"}, + {file = "dopt_basics-0.2.6-py3-none-any.whl", hash = "sha256:f0818e2f83e91fb7d398bcabfc6c420159757d7d093b20574b88a3abc24e3eab"}, + {file = "dopt_basics-0.2.6.tar.gz", hash = "sha256:0e90d0d7a711e0dee9f898574683442644d3145ac8905d38ea23775f62aa5d2b"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 877c05b..063e8df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "GUI for CRM of NAFKA project with WCE" authors = [ {name = "d-opt GmbH, resp. Florian Förster", email = "f.foerster@d-opt.com"}, ] -dependencies = ["pyside6>=6.11.0", "sqlalchemy[asyncio]>=2.0.50", "polars>=1.40.1", "dopt-basics>=0.2.5", "pydantic[email]>=2.13.4", "babel>=2.18.0", "python-dotenv>=1.2.2"] +dependencies = ["pyside6>=6.11.0", "sqlalchemy[asyncio]>=2.0.50", "polars>=1.40.1", "dopt-basics>=0.2.6", "pydantic[email]>=2.13.4", "babel>=2.18.0", "python-dotenv>=1.2.2"] requires-python = "<3.14,>=3.11" readme = "README.md" license = {text = "LicenseRef-Proprietary"} -- 2.34.1 From 72a0e52e0826c7e1e477f7196b9c7234f7fe501a Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 10:44:18 +0200 Subject: [PATCH 02/10] add list of env variables to README in source directory --- src/wce_crm/{env_vars.txt => README.md} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename src/wce_crm/{env_vars.txt => README.md} (90%) diff --git a/src/wce_crm/env_vars.txt b/src/wce_crm/README.md similarity index 90% rename from src/wce_crm/env_vars.txt rename to src/wce_crm/README.md index 00c2f3e..064f329 100644 --- a/src/wce_crm/env_vars.txt +++ b/src/wce_crm/README.md @@ -1,5 +1,7 @@ +# List of environment variables + DOPT_DEVELOPMENT: flag which signals that the current environment is in development mode DOPT_STOP_FOLDER_NAME: stop folder to find base path DOPT_DB_CRM: path to CRM database, relative to base path DOPT_DB_MAIN: path to main database, relative to base path -DOPT_PATH_LOGGING: path to logging folder, relative to base path \ No newline at end of file +DOPT_PATH_LOGGING: path to logging folder, relative to base path -- 2.34.1 From 9ddc1f5b990d5db27f8ed4875ebba17f95433a9d Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 10:44:50 +0200 Subject: [PATCH 03/10] additional logging and error handling --- src/wce_crm/backend/backend.py | 9 +++++ src/wce_crm/gui.py | 61 +++++++++++++++++++++++++++++----- src/wce_crm/logging.py | 12 +++++-- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/wce_crm/backend/backend.py b/src/wce_crm/backend/backend.py index a2c5605..a6e669e 100644 --- a/src/wce_crm/backend/backend.py +++ b/src/wce_crm/backend/backend.py @@ -8,6 +8,7 @@ import polars as pl import sqlalchemy as sql from wce_crm import db +from wce_crm.logging import logger_back as logger class CompanyInfo(TypedDict): @@ -83,6 +84,7 @@ def _transform_for_gui_output( def comp_search_choices() -> tuple[tuple[str, int], ...]: # TODO no reload functionality + logger.debug("[Call backend] comp_search_choices") q = db.DF_CRM_MASTER.lazy() counter = pl.int_range(0, pl.len()).over(pl.col.ma_unternehmensname) q = q.with_columns( @@ -98,6 +100,7 @@ def comp_search_choices() -> tuple[tuple[str, int], ...]: def comp_search_get_info( ma_id: int, ) -> CompanyInfo: + logger.debug("[Call backend] comp_search_get_info") df = db.DF_CRM_MASTER.filter(pl.col.ma_id == ma_id) if df.height > 1 or df.height == 0: raise ValueError(f"Größe des zurückgelieferten Datenpakets ungültig: {df.height}") @@ -111,6 +114,7 @@ def contact_person_search_choices( use_both_names: bool, ) -> tuple[tuple[str, int], ...]: # TODO no reload functionality + logger.debug("[Call backend] contact_person_search_choices") q = db.DF_CONTACT_PERSON.lazy() if ma_id is not None: q = q.filter(pl.col.ma_id == ma_id) @@ -136,6 +140,7 @@ def contact_person_search_choices( def contact_person_search_get_info( an_id: int, ) -> ContactPersonInfo: + logger.debug("[Call backend] contact_person_search_get_info") df = db.DF_CONTACT_PERSON.filter(pl.col.an_id == an_id) if df.height > 1 or df.height == 0: raise ValueError(f"Größe des zurückgelieferten Datenpakets ungültig: {df.height}") @@ -147,6 +152,7 @@ def contact_person_search_get_info( def insert_initial_recording( data: dict[str, Any], ) -> None: + logger.debug("[Call backend] insert_initial_recording") stmt = db.grunderfassung_unternehmen.insert().values(data) with db.ENGINE.begin() as conn: conn.execute(stmt) @@ -156,6 +162,7 @@ def update_initial_recording( id_: int, data: dict[str, Any], ) -> None: + logger.debug("[Call backend] update_initial_recording") stmt = ( db.grunderfassung_unternehmen.update() .where(db.grunderfassung_unternehmen.c.erfassung_id == id_) @@ -168,6 +175,7 @@ def update_initial_recording( def get_initial_recording( id_: int, ) -> dict[str, Any]: + logger.debug("[Call backend] get_initial_recording") stmt = db.grunderfassung_unternehmen.select().where( db.grunderfassung_unternehmen.c.erfassung_id == id_ ) @@ -190,6 +198,7 @@ class FrontpageCompany: def get_company_list() -> list[FrontpageCompany]: + logger.debug("[Call backend] get_company_list") stmt = sql.select( db.grunderfassung_unternehmen.c.erfassung_id, db.grunderfassung_unternehmen.c.Partnersuche__un_suche, diff --git a/src/wce_crm/gui.py b/src/wce_crm/gui.py index 0cae7d7..f537d21 100644 --- a/src/wce_crm/gui.py +++ b/src/wce_crm/gui.py @@ -8,6 +8,7 @@ import json import pickle import re import sys +import traceback import uuid from collections import defaultdict from collections.abc import Container, Iterable, Sequence @@ -33,7 +34,9 @@ from PySide6.QtCore import ( QObject, Qt, QTimer, + QtMsgType, Signal, + qInstallMessageHandler, ) from PySide6.QtGui import QAction from PySide6.QtWidgets import ( @@ -1073,11 +1076,11 @@ def set_form_data( value = data else: if key not in data: - logger_gui.debug("---- Key not in data: %s") - logger_gui.debug("-------- Data: %s", pformat(data)) + logger_gui.error("Key not in data: %s", key) + logger_gui.error("Data:\n%s", pformat(data)) value = data[key] - logger_gui.debug("---- Key set: %s", key) + logger_gui.debug("Key set: %s", key) set_widget_value(widget, value) @@ -3634,9 +3637,51 @@ class MainWindow(QMainWindow): help_menu.addAction(about_action) +# ** global exception handling +def global_exception_handler(exc_type, exc_value, exc_traceback): + """catches all unhandled errors""" + + # format error + error_msg = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback)) + logger_gui.critical(f"UNEXPECTED ERROR:\n{error_msg}") + + # message to user + # check if QApplication exists otherwise crashing of crash handler possible) + if QApplication.instance(): + msg_box = QMessageBox() + msg_box.setIcon(QMessageBox.Icon.Critical) + msg_box.setWindowTitle("Kritischer Fehler") + msg_box.setText( + "Ein unerwarteter Fehler ist aufgetreten. Die Details wurden protokolliert." + ) + # details for user or screenshots + msg_box.setDetailedText(error_msg) + msg_box.exec() + + sys.exit(1) + + +def qt_message_handler(mode, context, message): + """forwards internal Qt C++ warnings to Python loggers""" + if mode == QtMsgType.QtInfoMsg: + logger_gui.info(message) + elif mode == QtMsgType.QtWarningMsg: + logger_gui.warning(message) + elif mode == QtMsgType.QtCriticalMsg: + logger_gui.error(message) + elif mode == QtMsgType.QtFatalMsg: + logger_gui.critical(message) + + if __name__ == "__main__": - app = QApplication(sys.argv) - app.setStyleSheet(QSS) - window = MainWindow() - window.show() - sys.exit(app.exec()) + sys.excepthook = global_exception_handler + qInstallMessageHandler(qt_message_handler) + + try: + app = QApplication(sys.argv) + app.setStyleSheet(QSS) + window = MainWindow() + window.show() + sys.exit(app.exec()) + except Exception as err: + logger_gui.critical("Fehler beim Starten der Anwendung:\n%s", str(err), exc_info=True) diff --git a/src/wce_crm/logging.py b/src/wce_crm/logging.py index 5f6e855..c8afd46 100644 --- a/src/wce_crm/logging.py +++ b/src/wce_crm/logging.py @@ -1,6 +1,6 @@ import logging -from dopt_basics.logging import BASE_LOGGER, setup_logging +from dopt_basics.logging import BASE_LOGGER, LoggingConfig, setup_logging from wce_crm.constants import Config @@ -14,12 +14,16 @@ if Config.DEVELOPMENT_STATE: if not Config.PATH_LOGGING.exists(): Config.PATH_LOGGING.mkdir() -setup_logging( +LOGGING_CFG: LoggingConfig = LoggingConfig( enable_stderr=enable_stderr, enable_file=enable_file, logging_dir=Config.PATH_LOGGING, log_filename=Config.LOG_FILENAME, + file_max_bytes=10_485_760, + file_backup_count=2, ) + +setup_logging(LOGGING_CFG) logger_base = BASE_LOGGER.getChild("wce_crm") # ** GUI @@ -31,3 +35,7 @@ logger_get_data = logger_gui.getChild("get_data") logger_get_data.setLevel(logging.DEBUG) logger_auto_form = logger_gui.getChild("get_data_auto_form") logger_auto_form.setLevel(logging.DEBUG) + +# ** Backend +logger_back = logger_base.getChild("backend") +logger_back.setLevel(logging.DEBUG) -- 2.34.1 From 718ecaa2e3f935a378bdf40fec8722e4fac99b32 Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 11:12:08 +0200 Subject: [PATCH 04/10] updated logging --- src/wce_crm/gui.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/wce_crm/gui.py b/src/wce_crm/gui.py index f537d21..2a915f4 100644 --- a/src/wce_crm/gui.py +++ b/src/wce_crm/gui.py @@ -367,14 +367,26 @@ def get_leaf_dicts(data): def pprint_registry(widget_registry: WidgetRegistry) -> None: - print("---\n\n>>> Widget registry:") + print("\n\n>>> Widget registry:") for key, entry in widget_registry.items(): print(f"Key: {key}") - print(f"\twidget: {entry['widget']}") - print(f"\tfield key: {entry['form_field'].key}") + print(f"\twidget: {entry['widget']}") + print(f"\tfield key: {entry['form_field'].key}") print(f"\tfield type: {entry['form_field'].type}") +def pformat_registry(widget_registry: WidgetRegistry) -> str: + lines: list[str] = [] + lines.append("\n\n>>> Widget registry:") + for key, entry in widget_registry.items(): + lines.append(f"Key: {key}") + lines.append(f"\twidget: {entry['widget']}") + lines.append(f"\tfield key: {entry['form_field'].key}") + lines.append(f"\tfield type: {entry['form_field'].type}") + + return "\n".join(lines) + + class FormFieldType(enum.StrEnum): GROUP = enum.auto() TEXT = enum.auto() @@ -1719,6 +1731,11 @@ class AutoForm(QWidget): self.current_id: int = -1 + logger_auto_form.debug( + "Initialised Auto Form Widget. Registry:%s", + pformat_registry(self.widget_registry), + ) + def _print_registry(self) -> None: pprint_registry(self.widget_registry) @@ -2475,7 +2492,9 @@ class NewEntrySelect_view(QWidget): btn_person.setFixedWidth(300) btn_person.setFixedHeight(40) btn_person.clicked.connect(lambda: self.person_requested.emit()) - btn_person.clicked.connect(lambda: print("Person gewählt")) + btn_person.clicked.connect( + lambda: logger_gui.info("[Grunderfassung: Dummy Individualperson] Person gewählt") + ) layout.addWidget(back_btn) layout.addSpacing(15) -- 2.34.1 From 4b45b035e700a24bd0dd973f06cce2a20d1d7311 Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 11:21:33 +0200 Subject: [PATCH 05/10] add documentation including build process --- .gitignore | 2 ++ README.md | 26 ++++++++++++++++++++++++-- docs/Versionshistorie.md | 5 +++++ scripts/build.ps1 | 21 +++++++++++++++++++++ scripts/cvt_docs.ps1 | 3 +++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 docs/Versionshistorie.md create mode 100644 scripts/cvt_docs.ps1 diff --git a/.gitignore b/.gitignore index 8c72241..5f2bf0a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ reports/ CREDENTIALS* *.pkl *.pickle +# docs +*.pdf # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 83f30a6..925ae5c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ -# WCE: NAFKA --- GUI +# WCE/NAFKA: CRM GUI -... +## Allgemeine Hinweise + +Die vorliegende Anwendung befindet sich nach aktuellem Stand (Mai 2026) noch in einem sehr frühen Teststadium. Weder das Programmverhalten noch das Erscheinungsbild sind repräsentativ für das angestrebte Endergebnis. Es kann beim Testen zu Fehlern und Abstürzen kommen. Die Anwendung schreibt zwar selbstständig Fehlerlogs, dennoch ist es in Absturzfällen hilfreich, wenn hierzu ein kurzer Fehlerbericht erstellt wird. Dieser sollte die Schritte beinhalten, die durch den Nutzer erfolgt sind, damit der Fehler reproduziert und schlussendlich behoben werden kann. + +## Aktuelle Einschränkungen + +Dem Programm liegt aktuell ein hitorische Kopie der Kontaktliste vor, die für die Verknüpfung von Unternehmensdaten genutzt wird. Das bedeutet, dass sich Änderungen an der tatsächlichen, aktuell verwendeten Kontaktliste *nicht* in der Anwendung widerspiegeln. Die Anbindung an die in Betrieb befindliche Kontaktliste wird noch vorgenommen. + +Eine Nutzerverwatung gibt es aktuell noch nicht. Dementsprechend wird in der Anwendung auch nicht gespeichert, von wem die Änderungen zuletzt durchgeführt wurden. Ein Nutzerfeld existiert in der Oberfläche zwar, wird aber nicht gefüllt und bleibt aktuell noch stets leer. + +## Start der Anwendung + +Die Anwendung besteht aus einem Ordner, der alle relevanten Dateien zu ihrer Ausführung beinhaltet. Wenn der Ordner geöffnet wird, befinden sich darin die benötigten Unterordner und eine *Batch-Datei* (Endung ".bat"). Mit einem Doppelklick auf diese Datei, wird die Anwendung gestartet. Dies kann einige Sekunden in Anspruch nehmen. Sollte die Anwendung nach mehr als zehn Sekunden nicht gestartet sein, so wird vermutlich die Ausführung blockiert. In diesem Fall sind Ausführungsrichtlinien und Anti-Virus-Software durch die Administration zu prüfen. Sollte die Anwendung auch nach Tätigkeit der Administration nicht starten, so ist bitte *Kontakt zu d-opt* aufzunehmen, damit das Problem behoben werden kann. + +## Verwendung + +In der aktuellen Version ist die Grunderfassung für Unternehmen implementiert. Formularfelder und grundlegende Optik orientieren sich an den bereitgestellten Anforderungsdokumenten inkl. etwaiger Mock-Ups. Zur Anlage eines neuen Eintrags ist in der Übersicht oben die Schaltfläche "Neu" zu wählen. Anschließend kann die Grunderfassung für Unternehmen gewählt werden. Daraufhin öffnet sich eine Formularübersicht. Pflichtfelder sind mit einem Sternchen "*" gekennzeichnet. Sollten Pflichtfelder beim Speichern fehlen, so werden diese in einem Hinweisfenster benannt und im Formular selbst rot hervorgehoben. Das Formular kann mit den Knöpfen ganz unten gespeichert oder zurückgesetzt werden. Darüber hinaus sind Tastenkürzel für beide Aktionen hinterlegt: "Strg + s" für das Speichern und "Strg + z" für das Zurücksetzen. + +Sind alle Felder gefüllt, werden im Hintergrund die Daten nach dem Auslösen des Speichervorgangs nochmals validiert. Beispielsweise werden erfasste E-Mail-Adressen auf Gültigkeit geprüft. Etwaige Fehler werden dem Nutzer ebenfalls angezeigt. + +Nach der erfolgreichen Speicherung kann über die Schaltfläche im oberen Bereich "zurück zur Übersicht" auf die Hauptseite gewechselt werden. Dort sollte nun der neu angelegte Eintrag sichtbar sein. Die Übersicht ist absteigend nach dem Datum der letzten Änderung sortiert. Ein eben gespeicherter Eintrag sollte sich demzufolge ganz oben in der Übersicht befinden. Mit einem Klick auf einen Eintrag in der Übersicht wird erneut das Formular zur Grunderfassung mit den hinterlegten Daten geladen und angezeigt. Es können Änderungen vorgenommen und gespeichert werden. + +*Die Grunderfassung für Individualpersonen ist aktuell nur als Dummy verfügbar. Bei einem Klick auf die entsprechende Schaltfläche passiert nichts.* diff --git a/docs/Versionshistorie.md b/docs/Versionshistorie.md new file mode 100644 index 0000000..dfe1622 --- /dev/null +++ b/docs/Versionshistorie.md @@ -0,0 +1,5 @@ +# Versionshistorie (Changelog) + +## 29.05.2026 (Version: v0.1.1dev) (Tag: ExtTest-20260529) + +- initiale Version für erste Feedback-Schleife diff --git a/scripts/build.ps1 b/scripts/build.ps1 index b66bc4e..f75fa5d 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -8,6 +8,7 @@ $ENV_PATH = 'B:\deployments\WCE-NAFKA\dopt_nafka_wce-crm' $PY_PATH = Join-Path -Path $ENV_PATH -ChildPath 'python' $SRC_PATH = (Get-Location).Path + Write-Output "Build Pipeline for d-opt WCE/NAFKA project" Write-Output "Delete existing artifacts..." @@ -108,6 +109,26 @@ if ($? -eq $false){ } Write-Output "Copied startup scripts successfully" +# documentation +Write-Output "Generate manual..." +Set-Location $SRC_PATH +# $doc_build_script = Join-Path -Path $SRC_PATH -ChildPath "scripts\cvt_docs.ps1" -Resolve +.\scripts\cvt_docs.ps1 +if ($? -eq $false){ + Write-Output "[PWSH] Exiting script because there errors while generating the doc files" + Exit +} +Write-Output "Generated doc files successfully" +Write-Output "Copying manual file..." +$docs_src_path = Join-Path -Path $SRC_PATH -ChildPath 'docs\*.pdf' +# $docs_dest_path = Join-Path -Path $ENV_PATH -ChildPath 'manual' +Copy-Item -Path $docs_src_path -Destination $ENV_PATH -Force +if ($? -eq $false){ + Write-Output "[PWSH] Exiting script because there were errors while copying the doc files" + Exit +} +Write-Output "Copied doc files successfully" +Set-Location $ENV_PATH # env preparation Write-Output "Preparing environment with cleanup and pre-compilation..." diff --git a/scripts/cvt_docs.ps1 b/scripts/cvt_docs.ps1 new file mode 100644 index 0000000..59df0b4 --- /dev/null +++ b/scripts/cvt_docs.ps1 @@ -0,0 +1,3 @@ +# convert README Markdown file to PDF as a manual +pandoc .\README.md -o .\docs\Kurzanleitung.pdf -V geometry:"a4paper, margin=2.5cm" -V header-includes="\usepackage[none]{hyphenat}" +pandoc .\docs\Versionshistorie.md -o .\docs\Versionshistorie.pdf -V geometry:"a4paper, margin=2.5cm" -V header-includes="\usepackage[none]{hyphenat}" -- 2.34.1 From d0bee559dd2923443d6911574708a6b9a5b7ff0e Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 11:31:20 +0200 Subject: [PATCH 06/10] small fixes --- scripts/build.ps1 | 54 ++++++++++++++------------------------------ scripts/cvt_docs.ps1 | 4 ++-- src/wce_crm/gui.py | 1 - 3 files changed, 19 insertions(+), 40 deletions(-) diff --git a/scripts/build.ps1 b/scripts/build.ps1 index f75fa5d..844618f 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -42,23 +42,23 @@ if ($? -eq $false){ } Write-Output "Built package successfully" -# manual -# Write-Output "Generate manual..." -# .\scripts\cvt_manual.ps1 -# if ($? -eq $false){ -# Write-Output "[PWSH] Exiting script because there errors while generating the README file" -# Exit -# } -# Write-Output "Generated manual file successfully" -# Write-Output "Copying manual file..." -# $readme_src_path = Join-Path -Path $SRC_PATH -ChildPath 'docs\manual.pdf' -# $readme_dest_path = Join-Path -Path $ENV_PATH -ChildPath 'manual' -# Copy-Item -Path $readme_src_path -Destination $readme_dest_path -Force -# if ($? -eq $false){ -# Write-Output "[PWSH] Exiting script because there were errors while copying the manual file" -# Exit -# } -# Write-Output "Copied manual file successfully" +# documentation +Write-Output "Generate docs..." +# $doc_build_script = Join-Path -Path $SRC_PATH -ChildPath "scripts\cvt_docs.ps1" -Resolve +.\scripts\cvt_docs.ps1 +if ($? -eq $false){ + Write-Output "[PWSH] Exiting script because there errors while generating the doc files" + Exit +} +Write-Output "Generated doc files successfully" +Write-Output "Copying doc files..." +$docs_src_path = Join-Path -Path $SRC_PATH -ChildPath 'docs\*.pdf' +Copy-Item -Path $docs_src_path -Destination $ENV_PATH -Force +if ($? -eq $false){ + Write-Output "[PWSH] Exiting script because there were errors while copying the doc files" + Exit +} +Write-Output "Copied doc files successfully" Write-Output "Go into env directory..." @@ -109,26 +109,6 @@ if ($? -eq $false){ } Write-Output "Copied startup scripts successfully" -# documentation -Write-Output "Generate manual..." -Set-Location $SRC_PATH -# $doc_build_script = Join-Path -Path $SRC_PATH -ChildPath "scripts\cvt_docs.ps1" -Resolve -.\scripts\cvt_docs.ps1 -if ($? -eq $false){ - Write-Output "[PWSH] Exiting script because there errors while generating the doc files" - Exit -} -Write-Output "Generated doc files successfully" -Write-Output "Copying manual file..." -$docs_src_path = Join-Path -Path $SRC_PATH -ChildPath 'docs\*.pdf' -# $docs_dest_path = Join-Path -Path $ENV_PATH -ChildPath 'manual' -Copy-Item -Path $docs_src_path -Destination $ENV_PATH -Force -if ($? -eq $false){ - Write-Output "[PWSH] Exiting script because there were errors while copying the doc files" - Exit -} -Write-Output "Copied doc files successfully" -Set-Location $ENV_PATH # env preparation Write-Output "Preparing environment with cleanup and pre-compilation..." diff --git a/scripts/cvt_docs.ps1 b/scripts/cvt_docs.ps1 index 59df0b4..9f41dd9 100644 --- a/scripts/cvt_docs.ps1 +++ b/scripts/cvt_docs.ps1 @@ -1,3 +1,3 @@ # convert README Markdown file to PDF as a manual -pandoc .\README.md -o .\docs\Kurzanleitung.pdf -V geometry:"a4paper, margin=2.5cm" -V header-includes="\usepackage[none]{hyphenat}" -pandoc .\docs\Versionshistorie.md -o .\docs\Versionshistorie.pdf -V geometry:"a4paper, margin=2.5cm" -V header-includes="\usepackage[none]{hyphenat}" +pandoc .\README.md -o .\docs\01_Kurzanleitung.pdf -V geometry:"a4paper, margin=2.5cm" -V header-includes="\usepackage[none]{hyphenat}" +pandoc .\docs\Versionshistorie.md -o .\docs\02_Versionshistorie.pdf -V geometry:"a4paper, margin=2.5cm" -V header-includes="\usepackage[none]{hyphenat}" diff --git a/src/wce_crm/gui.py b/src/wce_crm/gui.py index 2a915f4..166713d 100644 --- a/src/wce_crm/gui.py +++ b/src/wce_crm/gui.py @@ -3330,7 +3330,6 @@ CONFIG_GRUNDERFASSUNG_UNTERNEHMEN: Final[AutoFormConfig] = AutoFormConfig( ) -# TODO clean code class PageFormCompany(QWidget): back_main_requested = Signal() # back to main page back_requested = Signal() # back button -- 2.34.1 From 65e80290968bc5918d9da4477599fe878c7b10ca Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 11:31:32 +0200 Subject: [PATCH 07/10] bump version --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 063e8df..879ec6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "wce-crm" -version = "0.1.1dev4" +version = "0.1.1dev5" description = "GUI for CRM of NAFKA project with WCE" authors = [ {name = "d-opt GmbH, resp. Florian Förster", email = "f.foerster@d-opt.com"}, @@ -71,7 +71,7 @@ directory = "reports/coverage" [tool.bumpversion] -current_version = "0.1.1dev4" +current_version = "0.1.1dev5" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. -- 2.34.1 From e1a8a9554806bb237026ce349686a429adeb18d7 Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 11:32:37 +0200 Subject: [PATCH 08/10] bump version for clean reference --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 879ec6d..3ed35ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "wce-crm" -version = "0.1.1dev5" +version = "0.1.1dev6" description = "GUI for CRM of NAFKA project with WCE" authors = [ {name = "d-opt GmbH, resp. Florian Förster", email = "f.foerster@d-opt.com"}, @@ -71,7 +71,7 @@ directory = "reports/coverage" [tool.bumpversion] -current_version = "0.1.1dev5" +current_version = "0.1.1dev6" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. -- 2.34.1 From 367dfe3d03507e92ebcbb3d29ccb564eb9b6d12d Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 11:59:51 +0200 Subject: [PATCH 09/10] additional custom logic --- pyproject.toml | 4 ++-- src/wce_crm/db.py | 4 +++- src/wce_crm/gui.py | 23 +++++++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3ed35ef..5f1caf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "wce-crm" -version = "0.1.1dev6" +version = "0.1.1dev7" description = "GUI for CRM of NAFKA project with WCE" authors = [ {name = "d-opt GmbH, resp. Florian Förster", email = "f.foerster@d-opt.com"}, @@ -71,7 +71,7 @@ directory = "reports/coverage" [tool.bumpversion] -current_version = "0.1.1dev6" +current_version = "0.1.1dev7" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/src/wce_crm/db.py b/src/wce_crm/db.py index 4d5cfd5..5cc67cb 100644 --- a/src/wce_crm/db.py +++ b/src/wce_crm/db.py @@ -357,7 +357,9 @@ grunderfassung_unternehmen: sql.Table = Table( Column("Kontaktperson__KP_funktion_beziehung", sql.Text, nullable=True), Column("Kontaktperson__KP_mobilfunknummer", sql.Text, nullable=True), Column("Kontaktperson__KP_name", sql.Text, nullable=True), - Column("Kontaktperson__KP_name_partner", sql.Text, nullable=True), + Column( + "Kontaktperson__KP_name_partner", sql.Text, nullable=True + ), # TODO: check if needed when set by trigger Column("Kontaktperson__KP_titel", sql.Text, nullable=True), Column("Kontaktperson__KP_vorname", sql.Text, nullable=True), Column("Partnersuche__kanal_aufmerksamkeit", sql.Text, nullable=True), diff --git a/src/wce_crm/gui.py b/src/wce_crm/gui.py index 166713d..f7f96a7 100644 --- a/src/wce_crm/gui.py +++ b/src/wce_crm/gui.py @@ -2653,7 +2653,7 @@ FORM_FIELDS_SEARCH_HEAD = [ FORM_FIELDS_CONTACT_PERSON = [ FormField( - "Name Unternehmen/Netzwerkpartner (pre-filled von Suche)", + "Name Unternehmen/Netzwerkpartner (vorausgefüllt von Suche)", FormFieldType.TEXT, key="KP_name_partner", required=False, @@ -3402,7 +3402,23 @@ class PageFormCompany(QWidget): container_layout.addSpacing(15) # --- CUSTOM LOGIC --- - # 'Bundesland' only if 'Inland' selected in 'Stammdaten' + # ** fill 'Kontaktperson -> Namen Unternehmen' + search_res = search_widgets_by_key(self.auto_form.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"] + ) + text_widget_set = search_widgets_by_key( + self.auto_form.widget_registry, f"Kontaktperson{COLUMN_SEP}KP_name_partner" + ) + assert len(text_widget_set) == 1 + self.text_widget_set = cast(QLineEdit, text_widget_set[0]["widget"]) + self.search_widget_trigger.textChanged.connect( + self._custom_set_company_name_contact_person + ) + + # ** 'Bundesland' only if 'Inland' selected in 'Stammdaten' person_location = search_widgets_by_key( self.auto_form.widget_registry, f"Stammdaten{COLUMN_SEP}aufenthaltsort" ) @@ -3420,6 +3436,9 @@ class PageFormCompany(QWidget): self.selection_county.setProperty("styleClass", "stempel") self.selection_county.setEnabled(False) + def _custom_set_company_name_contact_person(self, value: str) -> None: + self.text_widget_set.setText(str(value)) + def _custom_county_selection(self, idx: int) -> None: value = self.person_location.itemData(idx) if value == "Inland": -- 2.34.1 From 15af78900feb841b97cef08a0bc9ef62ea25ec7b Mon Sep 17 00:00:00 2001 From: foefl Date: Fri, 29 May 2026 12:03:10 +0200 Subject: [PATCH 10/10] fix list --- src/wce_crm/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wce_crm/README.md b/src/wce_crm/README.md index 064f329..4ea9ef6 100644 --- a/src/wce_crm/README.md +++ b/src/wce_crm/README.md @@ -1,7 +1,7 @@ # List of environment variables -DOPT_DEVELOPMENT: flag which signals that the current environment is in development mode -DOPT_STOP_FOLDER_NAME: stop folder to find base path -DOPT_DB_CRM: path to CRM database, relative to base path -DOPT_DB_MAIN: path to main database, relative to base path -DOPT_PATH_LOGGING: path to logging folder, relative to base path +- DOPT_DEVELOPMENT: flag which signals that the current environment is in development mode +- DOPT_STOP_FOLDER_NAME: stop folder to find base path +- DOPT_DB_CRM: path to CRM database, relative to base path +- DOPT_DB_MAIN: path to main database, relative to base path +- DOPT_PATH_LOGGING: path to logging folder, relative to base path -- 2.34.1