feat: extend inventory and administration workflows

This commit is contained in:
Marcel Peterkau
2026-07-22 22:26:02 +02:00
parent aeaaca1459
commit 070684d9bc
17 changed files with 1342 additions and 69 deletions
+139 -19
View File
@@ -124,13 +124,31 @@ class NewAssetDialog(tk.Toplevel):
super().__init__(master)
self.repository = repository
self.on_created = on_created
self.members = repository.list_members()
self.member_labels = {
f"{member.member_number or member.member_id} · {member.display_name}": member.member_id
for member in self.members
}
self.title("Neues Asset")
self.transient(master.winfo_toplevel())
self.resizable(False, False)
self.variables = {
name: tk.StringVar()
for name in ("label", "category", "inventory_number", "serial_number", "deposit_amount_default")
for name in (
"label",
"category",
"inventory_number",
"serial_number",
"deposit_amount_default",
"location",
"handed_over_at",
"condition",
"estimated_value",
"owner_name",
)
}
self.owner_type_var = tk.StringVar(value="VEREIN")
self.owner_member_var = tk.StringVar()
self._build_ui()
self.bind("<Escape>", lambda _event: self.destroy())
self.bind("<Return>", lambda _event: self._create())
@@ -145,6 +163,10 @@ class NewAssetDialog(tk.Toplevel):
("Inventarnummer", "inventory_number"),
("Seriennummer", "serial_number"),
("Kaution (EUR)", "deposit_amount_default"),
("Standort", "location"),
("Übergabedatum", "handed_over_at"),
("Zustand bei Übergabe", "condition"),
("Wert / Kaufpreis", "estimated_value"),
]
self.entries: dict[str, ttk.Entry] = {}
for row, (label, key) in enumerate(fields):
@@ -152,17 +174,42 @@ class NewAssetDialog(tk.Toplevel):
entry = ttk.Entry(frame, textvariable=self.variables[key], width=38)
entry.grid(row=row, column=1, sticky="ew", pady=5)
self.entries[key] = entry
owner_row = len(fields)
ttk.Label(frame, text="Eigentümer").grid(row=owner_row, column=0, sticky="w", pady=5, padx=(0, 12))
ttk.Combobox(
frame,
textvariable=self.owner_type_var,
values=("VEREIN", "MITGLIED", "EXTERN"),
state="readonly",
width=35,
).grid(row=owner_row, column=1, sticky="ew", pady=5)
ttk.Label(frame, text="Eigentümer-Mitglied").grid(
row=owner_row + 1, column=0, sticky="w", pady=5, padx=(0, 12)
)
ttk.Combobox(
frame,
textvariable=self.owner_member_var,
values=list(self.member_labels),
state="readonly",
width=35,
).grid(row=owner_row + 1, column=1, sticky="ew", pady=5)
ttk.Label(frame, text="Externer Eigentümer").grid(
row=owner_row + 2, column=0, sticky="w", pady=5, padx=(0, 12)
)
ttk.Entry(frame, textvariable=self.variables["owner_name"], width=38).grid(
row=owner_row + 2, column=1, sticky="ew", pady=5
)
ttk.Label(frame, text="Interne Notiz").grid(
row=len(fields),
row=owner_row + 3,
column=0,
sticky="nw",
pady=5,
padx=(0, 12),
)
self.notes_text = tk.Text(frame, width=38, height=5, wrap="word")
self.notes_text.grid(row=len(fields), column=1, sticky="ew", pady=5)
self.notes_text.grid(row=owner_row + 3, column=1, sticky="ew", pady=5)
buttons = ttk.Frame(frame)
buttons.grid(row=len(fields) + 1, column=0, columnspan=2, sticky="e", pady=(16, 0))
buttons.grid(row=owner_row + 4, column=0, columnspan=2, sticky="e", pady=(16, 0))
ttk.Button(buttons, text="Abbrechen", command=self.destroy).pack(side="left", padx=(0, 8))
ttk.Button(
buttons,
@@ -176,9 +223,15 @@ class NewAssetDialog(tk.Toplevel):
_activate_modal_window(self, self.entries.get("label"))
def _create(self) -> None:
owner_type = {"VEREIN": "club", "MITGLIED": "member", "EXTERN": "external"}[self.owner_type_var.get()]
try:
asset = self.repository.create_asset(
**{key: variable.get() for key, variable in self.variables.items()},
**{key: variable.get() for key, variable in self.variables.items() if key != "owner_name"},
owner_type=owner_type,
owner_member_id=(
self.member_labels.get(self.owner_member_var.get(), "") if owner_type == "member" else ""
),
owner_name=self.variables["owner_name"].get() if owner_type == "external" else "",
notes=self.notes_text.get("1.0", "end-1c"),
)
except RepositoryError as exc:
@@ -201,6 +254,19 @@ class EditAssetDialog(tk.Toplevel):
self.asset_id = asset_id
self.on_saved = on_saved
self.asset = repository.get_asset(asset_id)
self.members = repository.list_members()
self.member_labels = {
f"{member.member_number or member.member_id} · {member.display_name}": member.member_id
for member in self.members
}
owner_member_label = next(
(
label
for label, member_id in self.member_labels.items()
if member_id == self.asset.owner_member_id
),
"",
)
self.title("Asset bearbeiten")
self.transient(master.winfo_toplevel())
self.resizable(False, False)
@@ -211,7 +277,18 @@ class EditAssetDialog(tk.Toplevel):
"serial_number": tk.StringVar(value=self.asset.serial_number),
"deposit_amount_default": tk.StringVar(value=self.asset.deposit_amount_default),
"status": tk.StringVar(value=self.asset.status),
"location": tk.StringVar(value=self.asset.location),
"handed_over_at": tk.StringVar(value=self.asset.handed_over_at),
"condition": tk.StringVar(value=self.asset.condition),
"estimated_value": tk.StringVar(value=self.asset.estimated_value),
"owner_name": tk.StringVar(value=self.asset.owner_name),
}
self.owner_type_var = tk.StringVar(
value={"club": "VEREIN", "member": "MITGLIED", "external": "EXTERN"}.get(
self.asset.owner_type, "VEREIN"
)
)
self.owner_member_var = tk.StringVar(value=owner_member_label)
self._build_ui()
self.bind("<Escape>", lambda _event: self.destroy())
self.bind("<Return>", lambda _event: self._save())
@@ -227,6 +304,10 @@ class EditAssetDialog(tk.Toplevel):
("Inventarnummer", "inventory_number"),
("Seriennummer", "serial_number"),
("Kaution (EUR)", "deposit_amount_default"),
("Standort", "location"),
("Übergabedatum", "handed_over_at"),
("Zustand bei Übergabe", "condition"),
("Wert / Kaufpreis", "estimated_value"),
]
self.entries: dict[str, ttk.Entry] = {}
for row, (label, key) in enumerate(fields):
@@ -235,12 +316,40 @@ class EditAssetDialog(tk.Toplevel):
entry = ttk.Entry(frame, textvariable=self.variables[key], width=38, state=state)
entry.grid(row=row, column=1, sticky="ew", pady=5)
self.entries[key] = entry
ttk.Label(frame, text="Status").grid(row=len(fields), column=0, sticky="w", pady=5, padx=(0, 12))
status_values = [value for key, value in (
("available", "VERFUEGBAR"),
("lost", "VERLOREN"),
("retired", "AUSGEMUSTERT"),
)]
owner_row = len(fields)
ttk.Label(frame, text="Eigentümer").grid(row=owner_row, column=0, sticky="w", pady=5, padx=(0, 12))
ttk.Combobox(
frame,
textvariable=self.owner_type_var,
values=("VEREIN", "MITGLIED", "EXTERN"),
state="readonly",
width=35,
).grid(row=owner_row, column=1, sticky="ew", pady=5)
ttk.Label(frame, text="Eigentümer-Mitglied").grid(
row=owner_row + 1, column=0, sticky="w", pady=5, padx=(0, 12)
)
ttk.Combobox(
frame,
textvariable=self.owner_member_var,
values=list(self.member_labels),
state="readonly",
width=35,
).grid(row=owner_row + 1, column=1, sticky="ew", pady=5)
ttk.Label(frame, text="Externer Eigentümer").grid(
row=owner_row + 2, column=0, sticky="w", pady=5, padx=(0, 12)
)
ttk.Entry(frame, textvariable=self.variables["owner_name"], width=38).grid(
row=owner_row + 2, column=1, sticky="ew", pady=5
)
ttk.Label(frame, text="Status").grid(row=owner_row + 3, column=0, sticky="w", pady=5, padx=(0, 12))
status_values = [
value
for key, value in (
("available", "VERFUEGBAR"),
("lost", "VERLOREN"),
("retired", "AUSGEMUSTERT"),
)
]
self.status_map = {
"VERFUEGBAR": "available",
"VERLOREN": "lost",
@@ -260,8 +369,8 @@ class EditAssetDialog(tk.Toplevel):
state="readonly" if not issued else "disabled",
width=35,
)
self.status_box.grid(row=len(fields), column=1, sticky="ew", pady=5)
note_row = len(fields) + 1
self.status_box.grid(row=owner_row + 3, column=1, sticky="ew", pady=5)
note_row = owner_row + 4
ttk.Label(frame, text="Interne Notiz").grid(row=note_row, column=0, sticky="nw", pady=5, padx=(0, 12))
self.notes_text = tk.Text(frame, width=38, height=5, wrap="word")
self.notes_text.grid(row=note_row, column=1, sticky="ew", pady=5)
@@ -290,6 +399,21 @@ class EditAssetDialog(tk.Toplevel):
self.asset.inventory_number = self.variables["inventory_number"].get()
self.asset.serial_number = self.variables["serial_number"].get()
self.asset.notes = self.notes_text.get("1.0", "end-1c")
self.asset.location = self.variables["location"].get()
self.asset.handed_over_at = self.variables["handed_over_at"].get()
self.asset.condition = self.variables["condition"].get()
self.asset.estimated_value = self.variables["estimated_value"].get()
self.asset.owner_type = {"VEREIN": "club", "MITGLIED": "member", "EXTERN": "external"}[
self.owner_type_var.get()
]
self.asset.owner_member_id = (
self.member_labels.get(self.owner_member_var.get(), "")
if self.asset.owner_type == "member"
else ""
)
self.asset.owner_name = (
self.variables["owner_name"].get() if self.asset.owner_type == "external" else ""
)
if not self.asset.current_holder_member_id:
self.asset.deposit_amount_default = self.variables["deposit_amount_default"].get()
self.asset.status = self.status_map.get(self.status_var.get(), self.asset.status)
@@ -394,9 +518,7 @@ class IssueAssetDialog(tk.Toplevel):
self.member_tree.delete(*self.member_tree.get_children())
query = self.search_var.get().strip().casefold()
filtered = [
member
for member in self.members
if not query or query in self._member_search_text(member)
member for member in self.members if not query or query in self._member_search_text(member)
]
for member in filtered:
self.member_tree.insert(
@@ -599,9 +721,7 @@ class IntegrityWarningDialog(tk.Toplevel):
text="\n".join(f"{item}" for item in self.warnings),
style="Warning.TLabel",
justify="left",
).grid(
row=1, column=0, sticky="w", pady=(12, 0)
)
).grid(row=1, column=0, sticky="w", pady=(12, 0))
buttons = ttk.Frame(frame)
buttons.grid(row=2, column=0, sticky="e", pady=(18, 0))
ttk.Button(buttons, text="Nein", command=self.destroy).pack(side="left", padx=(0, 8))