Add asset records, claims, and credit workflows

This commit is contained in:
Marcel Peterkau
2026-06-26 23:03:06 +02:00
parent 30b6d253b2
commit d1dab793a6
11 changed files with 2060 additions and 10 deletions
+141 -3
View File
@@ -5,16 +5,17 @@ from tkinter import messagebox, ttk
from ccma import __version__
from ccma.config import AppConfig
from ccma.domain.models import HousekeeperFinding, Member
from ccma.domain.models import Asset, HousekeeperFinding, Member
from ccma.services.housekeeper import Housekeeper
from ccma.storage.repository import MemberRepository, RepositoryError
from ccma.ui.asset_tab import AssetTab
from ccma.ui.claim_tab import ClaimTab
from ccma.ui.dialogs import NewMemberDialog
from ccma.ui.dialogs import EditAssetDialog, IssueAssetDialog, NewAssetDialog, NewMemberDialog
from ccma.ui.icons import IconStore
from ccma.ui.member_tab import MemberTab
from ccma.ui.options_dialog import OptionsDialog
from ccma.ui.theme import load_theme
from ccma.ui.work_tabs import DashboardTab, HousekeeperTab, MembersTab, SearchResultsTab
from ccma.ui.work_tabs import AssetsTab, DashboardTab, HousekeeperTab, MembersTab, SearchResultsTab
class TabManager:
@@ -175,6 +176,17 @@ class MainWindow(ttk.Frame):
)
members_button.image = members_icon
members_button.pack(side="left", padx=(0, 6))
assets_icon = self.icons.get("key-variant", 24) or self.icons.get("package-variant-closed", 24)
assets_button = ttk.Button(
actions,
text="Inventar",
image=assets_icon,
compound="top",
width=14,
command=self.open_assets,
)
assets_button.image = assets_icon
assets_button.pack(side="left", padx=(0, 6))
new_icon = self.icons.get("account-plus", 24)
new_button = ttk.Button(
actions,
@@ -278,6 +290,9 @@ class MainWindow(ttk.Frame):
on_close=lambda: self.tabs.close(key),
on_changed=self.refresh_overview,
on_open_claim=self.open_claim,
on_open_assets=self.open_assets,
on_open_asset=self.open_asset,
on_return_asset=self.return_asset,
)
self.tabs.add(
key,
@@ -312,6 +327,34 @@ class MainWindow(ttk.Frame):
icon_name="receipt",
)
def open_asset(self, asset_id: str) -> None:
key = f"asset:{asset_id}"
if self.tabs.focus(key):
return
try:
asset = self.repository.get_asset(asset_id)
except RepositoryError as exc:
messagebox.showerror("Asset konnte nicht geöffnet werden", str(exc), parent=self)
return
tab = AssetTab(
self.notebook,
self.repository,
asset_id,
on_close=lambda: self.tabs.close(key),
on_changed=self.refresh_overview,
on_open_member=self.open_member,
on_issue_asset=self.issue_asset,
on_return_asset=self.return_asset,
on_open_claim=self.open_claim,
)
self.tabs.add(
key,
tab,
asset.label or asset.asset_id,
image=self.icons.get("key-variant", 16) or self.icons.get("package-variant-closed", 16),
icon_name="key-variant",
)
def _claim_changed(self, member_id: str) -> None:
member_tab = self.tabs.tabs.get(f"member:{member_id}")
if isinstance(member_tab, MemberTab) and member_tab.winfo_exists():
@@ -336,14 +379,91 @@ class MainWindow(ttk.Frame):
icon_name="account-group",
)
def open_assets(self) -> None:
key = "assets"
if self.tabs.focus(key):
return
tab = AssetsTab(
self.notebook,
self.repository.list_assets(),
self._member_reference_label,
self.new_asset,
self.open_asset,
self.edit_asset,
self.issue_asset,
self.return_asset,
lambda: self.tabs.close(key),
)
self.tabs.add(
key,
tab,
"Inventar",
image=self.icons.get("key-variant", 16) or self.icons.get("package-variant-closed", 16),
icon_name="key-variant",
)
def new_member(self) -> None:
NewMemberDialog(self, self.repository, self._member_created)
def new_asset(self) -> None:
NewAssetDialog(self, self.repository, self._asset_created)
def _member_created(self, member: Member) -> None:
self.refresh_overview()
self.open_member(member.member_id)
self.status_var.set(f"Mitgliederakte für {member.display_name} angelegt.")
def _asset_created(self, asset: Asset) -> None:
self.refresh_overview()
self.open_assets()
self.status_var.set(f"Asset angelegt: {asset.label}")
def issue_asset(self, asset_id: str, preselected_member_id: str = "") -> None:
try:
IssueAssetDialog(
self,
self.repository,
asset_id,
self._asset_changed,
preselected_member_id=preselected_member_id,
)
except (RepositoryError, tk.TclError) as exc:
messagebox.showerror("Asset konnte nicht ausgegeben werden", str(exc), parent=self)
def edit_asset(self, asset_id: str) -> None:
try:
EditAssetDialog(
self,
self.repository,
asset_id,
self._asset_changed,
)
except (RepositoryError, tk.TclError) as exc:
messagebox.showerror("Asset konnte nicht bearbeitet werden", str(exc), parent=self)
def return_asset(self, asset_id: str) -> None:
try:
asset = self.repository.get_asset(asset_id)
except RepositoryError as exc:
messagebox.showerror("Asset konnte nicht geladen werden", str(exc), parent=self)
return
if not messagebox.askyesno(
"Asset zurücknehmen",
f"{asset.label}\n\nDieses Asset wirklich als zurückgegeben markieren?",
parent=self,
):
return
try:
asset = self.repository.return_asset(asset_id)
except RepositoryError as exc:
messagebox.showerror("Rückgabe fehlgeschlagen", str(exc), parent=self)
return
self._asset_changed(asset)
def _asset_changed(self, asset: Asset) -> None:
self.refresh_overview()
self.status_var.set(f"Asset aktualisiert: {asset.label}")
def open_housekeeper(self) -> None:
key = "housekeeper"
if self.tabs.focus(key):
@@ -383,6 +503,14 @@ class MainWindow(ttk.Frame):
members_tab = self.tabs.tabs.get("members")
if isinstance(members_tab, MembersTab) and members_tab.winfo_exists():
members_tab.refresh(self.repository.list_members())
assets_tab = self.tabs.tabs.get("assets")
if isinstance(assets_tab, AssetsTab) and assets_tab.winfo_exists():
assets_tab.refresh(self.repository.list_assets())
for key, widget in self.tabs.tabs.items():
if key.startswith("member:") and isinstance(widget, MemberTab) and widget.winfo_exists():
widget.refresh()
if key.startswith("asset:") and isinstance(widget, AssetTab) and widget.winfo_exists():
widget.refresh()
if self.housekeeper_button and self.housekeeper_button.winfo_exists():
self.housekeeper_button.configure(text=f"Hausmeister ({len(self.findings)})")
@@ -422,3 +550,13 @@ class MainWindow(ttk.Frame):
"Der Store enthält ungültige Akten:\n\n" + "\n".join(self.validation_errors[:12]),
parent=self,
)
def _member_reference_label(self, member_id: str) -> str:
if not member_id:
return ""
try:
member = self.repository.get_member(member_id)
except RepositoryError:
return member_id
prefix = member.member_number or member.member_id
return f"{prefix} · {member.display_name or member.member_id}"