mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-07-01 03:04:52 +02:00
Fix ruff lint violations
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from ccma.domain.models import HOUSEKEEPER_MEMBER_FIELD_LABELS
|
||||
from ccma.domain.dates import DateValidationError, validate_member_dates
|
||||
from ccma.domain.models import HOUSEKEEPER_MEMBER_FIELD_LABELS
|
||||
from ccma.rules.api import RuleContext, task
|
||||
|
||||
RULE_ID = "birthdate-check"
|
||||
|
||||
@@ -44,7 +44,11 @@ def evaluate(context: RuleContext):
|
||||
)
|
||||
)
|
||||
|
||||
year_from = started_at.year if getattr(context.settings, "retroactive_claims", False) else context.today.year
|
||||
year_from = (
|
||||
started_at.year
|
||||
if getattr(context.settings, "retroactive_claims", False)
|
||||
else context.today.year
|
||||
)
|
||||
for year in range(year_from, context.today.year + 2):
|
||||
actions.extend(_membership_claims(context, started_at, accepted_at, year))
|
||||
return actions
|
||||
|
||||
@@ -11,7 +11,11 @@ CONTENT_HASH_FIELD = "content_hash"
|
||||
|
||||
def _hashable_copy(data: Any, *, hash_field: str = CONTENT_HASH_FIELD) -> Any:
|
||||
if isinstance(data, dict):
|
||||
return {key: _hashable_copy(value, hash_field=hash_field) for key, value in data.items() if key != hash_field}
|
||||
return {
|
||||
key: _hashable_copy(value, hash_field=hash_field)
|
||||
for key, value in data.items()
|
||||
if key != hash_field
|
||||
}
|
||||
if isinstance(data, list):
|
||||
return [_hashable_copy(item, hash_field=hash_field) for item in data]
|
||||
return data
|
||||
|
||||
@@ -20,7 +20,14 @@ from ccma.domain.contributions import (
|
||||
payment_allocated_total,
|
||||
)
|
||||
from ccma.domain.dates import DateValidationError, normalize_date_input, validate_member_dates
|
||||
from ccma.domain.models import ASSET_STATUS_LABELS, MEMBERSHIP_STATUS_LABELS, Asset, ContributionData, Event, Member
|
||||
from ccma.domain.models import (
|
||||
ASSET_STATUS_LABELS,
|
||||
MEMBERSHIP_STATUS_LABELS,
|
||||
Asset,
|
||||
ContributionData,
|
||||
Event,
|
||||
Member,
|
||||
)
|
||||
from ccma.storage.atomic import json_content_hash_matches, read_json, write_json_atomic
|
||||
|
||||
|
||||
@@ -143,7 +150,9 @@ class MemberRepository:
|
||||
try:
|
||||
config = read_json(self.root / "repository.json")
|
||||
if not json_content_hash_matches(config):
|
||||
errors.append("repository.json: Hash fehlt oder stimmt nicht; Datei wurde vermutlich extern geändert.")
|
||||
errors.append(
|
||||
"repository.json: Hash fehlt oder stimmt nicht; Datei wurde vermutlich extern geändert."
|
||||
)
|
||||
if int(config.get("schema_version", 0)) != 1:
|
||||
errors.append("repository.json: nicht unterstützte schema_version")
|
||||
policy = config.get("member_number_policy") or {}
|
||||
@@ -157,7 +166,10 @@ class MemberRepository:
|
||||
for member_dir in self._member_directories():
|
||||
try:
|
||||
member, _contributions = self.preflight_member_record(member_dir.name)
|
||||
errors.extend(f"{member_dir.name}/{warning}" for warning in self.member_hash_warnings(member_dir.name))
|
||||
errors.extend(
|
||||
f"{member_dir.name}/{warning}"
|
||||
for warning in self.member_hash_warnings(member_dir.name)
|
||||
)
|
||||
validate_member_dates(
|
||||
birth_date=member.birth_date,
|
||||
accepted_at=member.accepted_at,
|
||||
@@ -186,12 +198,16 @@ class MemberRepository:
|
||||
for asset_dir in self._asset_directories():
|
||||
try:
|
||||
asset = self.get_asset(asset_dir.name)
|
||||
errors.extend(f"{asset_dir.name}/{warning}" for warning in self.asset_hash_warnings(asset_dir.name))
|
||||
errors.extend(
|
||||
f"{asset_dir.name}/{warning}"
|
||||
for warning in self.asset_hash_warnings(asset_dir.name)
|
||||
)
|
||||
if asset.asset_id != asset_dir.name:
|
||||
errors.append(f"{asset_dir.name}/asset.json: asset_id stimmt nicht mit Ordner überein")
|
||||
if asset.schema_version != 1:
|
||||
errors.append(
|
||||
f"{asset_dir.name}/asset.json: nicht unterstützte schema_version {asset.schema_version}"
|
||||
f"{asset_dir.name}/asset.json: "
|
||||
f"nicht unterstützte schema_version {asset.schema_version}"
|
||||
)
|
||||
if asset.status not in ASSET_STATUS_LABELS:
|
||||
errors.append(f"{asset_dir.name}/asset.json: ungültiger Asset-Status")
|
||||
@@ -425,7 +441,9 @@ class MemberRepository:
|
||||
existing.current_holder_member_id
|
||||
and money_text(deposit_amount) != str(existing.deposit_amount_default)
|
||||
):
|
||||
raise RepositoryError("Die Kaution kann nur geändert werden, wenn das Asset nicht ausgegeben ist.")
|
||||
raise RepositoryError(
|
||||
"Die Kaution kann nur geändert werden, wenn das Asset nicht ausgegeben ist."
|
||||
)
|
||||
asset.label = asset.label.strip()
|
||||
asset.category = asset.category.strip()
|
||||
asset.inventory_number = asset.inventory_number.strip()
|
||||
@@ -1220,14 +1238,17 @@ class MemberRepository:
|
||||
try:
|
||||
member_raw = read_json(self._member_path(member_id) / "member.json")
|
||||
if not json_content_hash_matches(member_raw):
|
||||
warnings.append("member.json: Hash fehlt oder stimmt nicht; Datei wurde vermutlich extern geändert.")
|
||||
warnings.append(
|
||||
"member.json: Hash fehlt oder stimmt nicht; Datei wurde vermutlich extern geändert."
|
||||
)
|
||||
except (OSError, ValueError, TypeError, json.JSONDecodeError):
|
||||
pass
|
||||
try:
|
||||
contributions_raw = read_json(self._member_path(member_id) / "contributions.json")
|
||||
if not json_content_hash_matches(contributions_raw):
|
||||
warnings.append(
|
||||
"contributions.json: Hash fehlt oder stimmt nicht; Datei wurde vermutlich extern geändert."
|
||||
"contributions.json: Hash fehlt oder stimmt nicht; "
|
||||
"Datei wurde vermutlich extern geändert."
|
||||
)
|
||||
except (OSError, ValueError, TypeError, json.JSONDecodeError):
|
||||
pass
|
||||
@@ -1238,7 +1259,9 @@ class MemberRepository:
|
||||
try:
|
||||
asset_raw = read_json(self._asset_path(asset_id) / "asset.json")
|
||||
if not json_content_hash_matches(asset_raw):
|
||||
warnings.append("asset.json: Hash fehlt oder stimmt nicht; Datei wurde vermutlich extern geändert.")
|
||||
warnings.append(
|
||||
"asset.json: Hash fehlt oder stimmt nicht; Datei wurde vermutlich extern geändert."
|
||||
)
|
||||
except (OSError, ValueError, TypeError, json.JSONDecodeError):
|
||||
pass
|
||||
return warnings
|
||||
|
||||
@@ -5,7 +5,7 @@ from collections.abc import Callable
|
||||
from datetime import datetime
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
from ccma.domain.models import ASSET_STATUS_LABELS, Asset, Event
|
||||
from ccma.domain.models import ASSET_STATUS_LABELS, Event
|
||||
from ccma.storage.repository import MemberRepository, RepositoryError
|
||||
from ccma.ui.dialogs import AssetClaimDialog, IntegrityWarningDialog
|
||||
from ccma.ui.messages import MessageAction, MessageBannerList, TabMessage
|
||||
@@ -196,8 +196,16 @@ class AssetTab(ttk.Frame):
|
||||
ttk.Button(finance_actions, text="Verlustforderung", command=self._create_loss_claim).pack(
|
||||
side="left", padx=(0, 8)
|
||||
)
|
||||
ttk.Button(finance_actions, text="Reparaturforderung", command=self._create_repair_claim).pack(side="left")
|
||||
self.asset_claims = ttk.Treeview(finance_tab, columns=("title", "due", "amount", "member"), show="headings")
|
||||
ttk.Button(
|
||||
finance_actions,
|
||||
text="Reparaturforderung",
|
||||
command=self._create_repair_claim,
|
||||
).pack(side="left")
|
||||
self.asset_claims = ttk.Treeview(
|
||||
finance_tab,
|
||||
columns=("title", "due", "amount", "member"),
|
||||
show="headings",
|
||||
)
|
||||
for key, title, width in (
|
||||
("title", "Forderung", 240),
|
||||
("due", "Fällig", 110),
|
||||
@@ -429,7 +437,14 @@ class AssetTab(ttk.Frame):
|
||||
claim_type="asset_repair",
|
||||
)
|
||||
|
||||
def _open_claim_dialog(self, *, preset_title: str, preset_amount: str, preset_description: str, claim_type: str) -> None:
|
||||
def _open_claim_dialog(
|
||||
self,
|
||||
*,
|
||||
preset_title: str,
|
||||
preset_amount: str,
|
||||
preset_description: str,
|
||||
claim_type: str,
|
||||
) -> None:
|
||||
member_id = self.asset.current_holder_member_id
|
||||
if not member_id:
|
||||
messagebox.showerror(
|
||||
|
||||
@@ -261,12 +261,26 @@ class ClaimTab(ttk.Frame):
|
||||
for allocation in self.data.allocations
|
||||
if str(allocation.get("claim_id", "")) == self.claim_id and str(allocation.get("credit_id", ""))
|
||||
]
|
||||
allocated_credit_total = money_text(
|
||||
sum(
|
||||
(decimal_value(item.get("amount", "0")) for item in credit_allocations),
|
||||
Decimal("0"),
|
||||
)
|
||||
)
|
||||
credit_group = self.ledger.insert(
|
||||
"",
|
||||
"end",
|
||||
iid="group:credits",
|
||||
text=f"Gutschriften ({len(credit_allocations)})",
|
||||
values=("", "", "", "", f"{money_text(sum((decimal_value(item.get('amount', '0')) for item in credit_allocations), Decimal('0')))} EUR", "", ""),
|
||||
values=(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
f"{allocated_credit_total} EUR",
|
||||
"",
|
||||
"",
|
||||
),
|
||||
tags=("credit-group",),
|
||||
open=True,
|
||||
)
|
||||
|
||||
+79
-14
@@ -1,8 +1,7 @@
|
||||
import tkinter as tk
|
||||
from collections.abc import Callable
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
from datetime import date
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
from ccma.domain.dates import age_label, date_input_hint
|
||||
from ccma.domain.models import Asset, Member
|
||||
@@ -35,7 +34,15 @@ class NewMemberDialog(tk.Toplevel):
|
||||
self.number_policy = repository.get_member_number_policy()
|
||||
self.variables = {
|
||||
name: tk.StringVar()
|
||||
for name in ("first_name", "last_name", "nickname", "email", "phone", "birth_date", "member_number")
|
||||
for name in (
|
||||
"first_name",
|
||||
"last_name",
|
||||
"nickname",
|
||||
"email",
|
||||
"phone",
|
||||
"birth_date",
|
||||
"member_number",
|
||||
)
|
||||
}
|
||||
self._build_ui()
|
||||
self.bind("<Escape>", lambda _event: self.destroy())
|
||||
@@ -145,13 +152,24 @@ 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
|
||||
ttk.Label(frame, text="Interne Notiz").grid(row=len(fields), column=0, sticky="nw", pady=5, padx=(0, 12))
|
||||
ttk.Label(frame, text="Interne Notiz").grid(
|
||||
row=len(fields),
|
||||
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)
|
||||
buttons = ttk.Frame(frame)
|
||||
buttons.grid(row=len(fields) + 1, 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, text="Asset anlegen", style="Accent.TButton", command=self._create).pack(side="left")
|
||||
ttk.Button(
|
||||
buttons,
|
||||
text="Asset anlegen",
|
||||
style="Accent.TButton",
|
||||
command=self._create,
|
||||
).pack(side="left")
|
||||
self.after_idle(lambda: self.entries["label"].focus_set())
|
||||
|
||||
def _activate_modal(self) -> None:
|
||||
@@ -171,7 +189,13 @@ class NewAssetDialog(tk.Toplevel):
|
||||
|
||||
|
||||
class EditAssetDialog(tk.Toplevel):
|
||||
def __init__(self, master: tk.Misc, repository: MemberRepository, asset_id: str, on_saved: Callable[[Asset], None]):
|
||||
def __init__(
|
||||
self,
|
||||
master: tk.Misc,
|
||||
repository: MemberRepository,
|
||||
asset_id: str,
|
||||
on_saved: Callable[[Asset], None],
|
||||
):
|
||||
super().__init__(master)
|
||||
self.repository = repository
|
||||
self.asset_id = asset_id
|
||||
@@ -310,7 +334,12 @@ class IssueAssetDialog(tk.Toplevel):
|
||||
frame.rowconfigure(3, weight=1)
|
||||
asset = self.repository.get_asset(self.asset_id)
|
||||
ttk.Label(frame, text="Asset").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 12))
|
||||
ttk.Label(frame, text=asset.label, style="TimelineHeader.TLabel").grid(row=0, column=1, sticky="w", pady=5)
|
||||
ttk.Label(frame, text=asset.label, style="TimelineHeader.TLabel").grid(
|
||||
row=0,
|
||||
column=1,
|
||||
sticky="w",
|
||||
pady=5,
|
||||
)
|
||||
ttk.Label(frame, text="Suche").grid(row=1, column=0, sticky="w", pady=5, padx=(0, 12))
|
||||
self.search_entry = ttk.Entry(frame, textvariable=self.search_var, width=42)
|
||||
self.search_entry.grid(row=1, column=1, sticky="ew", pady=5)
|
||||
@@ -378,7 +407,11 @@ class IssueAssetDialog(tk.Toplevel):
|
||||
)
|
||||
selected_id = self.preselected_member_id if self.preselected_member_id else ""
|
||||
if filtered:
|
||||
target_id = selected_id if selected_id and any(member.member_id == selected_id for member in filtered) else filtered[0].member_id
|
||||
target_id = (
|
||||
selected_id
|
||||
if selected_id and any(member.member_id == selected_id for member in filtered)
|
||||
else filtered[0].member_id
|
||||
)
|
||||
self.member_tree.selection_set(target_id)
|
||||
self.member_tree.focus(target_id)
|
||||
self.member_tree.see(target_id)
|
||||
@@ -457,7 +490,12 @@ class AssetClaimDialog(tk.Toplevel):
|
||||
asset = self.repository.get_asset(self.asset_id)
|
||||
member = self.repository.get_member(self.member_id)
|
||||
ttk.Label(frame, text="Asset").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 12))
|
||||
ttk.Label(frame, text=asset.label, style="TimelineHeader.TLabel").grid(row=0, column=1, sticky="w", pady=5)
|
||||
ttk.Label(frame, text=asset.label, style="TimelineHeader.TLabel").grid(
|
||||
row=0,
|
||||
column=1,
|
||||
sticky="w",
|
||||
pady=5,
|
||||
)
|
||||
ttk.Label(frame, text="Mitglied").grid(row=1, column=0, sticky="w", pady=5, padx=(0, 12))
|
||||
ttk.Label(frame, text=f"{member.member_number or member.member_id} · {member.display_name}").grid(
|
||||
row=1, column=1, sticky="w", pady=5
|
||||
@@ -470,11 +508,23 @@ class AssetClaimDialog(tk.Toplevel):
|
||||
(f"Fällig am ({date_input_hint()}) *", "due_date"),
|
||||
)
|
||||
):
|
||||
ttk.Label(frame, text=label).grid(row=row_offset + index, column=0, sticky="w", pady=5, padx=(0, 12))
|
||||
ttk.Label(frame, text=label).grid(
|
||||
row=row_offset + index,
|
||||
column=0,
|
||||
sticky="w",
|
||||
pady=5,
|
||||
padx=(0, 12),
|
||||
)
|
||||
ttk.Entry(frame, textvariable=self.variables[key], width=42).grid(
|
||||
row=row_offset + index, column=1, sticky="ew", pady=5
|
||||
)
|
||||
ttk.Label(frame, text="Beschreibung").grid(row=row_offset + 3, column=0, sticky="nw", pady=5, padx=(0, 12))
|
||||
ttk.Label(frame, text="Beschreibung").grid(
|
||||
row=row_offset + 3,
|
||||
column=0,
|
||||
sticky="nw",
|
||||
pady=5,
|
||||
padx=(0, 12),
|
||||
)
|
||||
self.description_text = tk.Text(frame, width=42, height=5, wrap="word")
|
||||
self.description_text.grid(row=row_offset + 3, column=1, sticky="ew", pady=5)
|
||||
self.description_text.insert("1.0", self.preset_description)
|
||||
@@ -488,7 +538,12 @@ class AssetClaimDialog(tk.Toplevel):
|
||||
buttons = ttk.Frame(frame)
|
||||
buttons.grid(row=row_offset + 5, 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, text="Forderung anlegen", style="Accent.TButton", command=self._create).pack(side="left")
|
||||
ttk.Button(
|
||||
buttons,
|
||||
text="Forderung anlegen",
|
||||
style="Accent.TButton",
|
||||
command=self._create,
|
||||
).pack(side="left")
|
||||
self.after_idle(lambda: frame.focus_set())
|
||||
|
||||
def _activate_modal(self) -> None:
|
||||
@@ -539,13 +594,23 @@ class IntegrityWarningDialog(tk.Toplevel):
|
||||
"Haben Sie alle Daten geprüft und soll der Hash jetzt aktualisiert werden?"
|
||||
)
|
||||
ttk.Label(frame, text=message, justify="left").grid(row=0, column=0, sticky="w")
|
||||
ttk.Label(frame, text="\n".join(f"• {item}" for item in self.warnings), style="Warning.TLabel", justify="left").grid(
|
||||
ttk.Label(
|
||||
frame,
|
||||
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)
|
||||
)
|
||||
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))
|
||||
ttk.Button(buttons, text="Ja, bestätigen", style="Accent.TButton", command=self._confirm).pack(side="left")
|
||||
ttk.Button(
|
||||
buttons,
|
||||
text="Ja, bestätigen",
|
||||
style="Accent.TButton",
|
||||
command=self._confirm,
|
||||
).pack(side="left")
|
||||
|
||||
def _activate_modal(self) -> None:
|
||||
_activate_modal_window(self)
|
||||
|
||||
@@ -545,7 +545,9 @@ class MainWindow(ttk.Frame):
|
||||
self.tabs.refresh_icons(self.icons)
|
||||
|
||||
def _show_validation_warning(self) -> None:
|
||||
display_errors = [item for item in self.validation_errors if "Hash fehlt oder stimmt nicht" not in item]
|
||||
display_errors = [
|
||||
item for item in self.validation_errors if "Hash fehlt oder stimmt nicht" not in item
|
||||
]
|
||||
if not display_errors:
|
||||
return
|
||||
messagebox.showwarning(
|
||||
|
||||
@@ -8,17 +8,16 @@ from tkinter import messagebox, ttk
|
||||
|
||||
from ccma.domain.contributions import CLAIM_STATUS_LABELS, claim_status, claim_total, money_text
|
||||
from ccma.domain.dates import age_label, date_input_hint, format_date_for_display
|
||||
from ccma.domain.models import ASSET_STATUS_LABELS, MEMBERSHIP_STATUS_LABELS as STATUS_LABELS
|
||||
from ccma.domain.models import Event
|
||||
from ccma.domain.models import ASSET_STATUS_LABELS, Event
|
||||
from ccma.domain.models import MEMBERSHIP_STATUS_LABELS as STATUS_LABELS
|
||||
from ccma.storage.repository import MemberRepository, RepositoryError
|
||||
from ccma.ui.document_dialog import DocumentTemplateDialog
|
||||
from ccma.ui.dialogs import IntegrityWarningDialog
|
||||
from ccma.ui.document_dialog import DocumentTemplateDialog
|
||||
from ccma.ui.file_open import open_path
|
||||
from ccma.ui.labels import display_label, storage_key
|
||||
from ccma.ui.messages import MessageAction, MessageBannerList, TabMessage
|
||||
from ccma.ui.scrolling import ScrollableFrame
|
||||
|
||||
|
||||
CLAIM_TABLE_COLUMNS = (
|
||||
("title", "Forderung", 220),
|
||||
("due", "Fällig", 100),
|
||||
@@ -287,8 +286,12 @@ class MemberTab(ttk.Frame):
|
||||
ttk.Button(asset_actions, text="Asset öffnen", command=self._open_selected_asset).pack(
|
||||
side="left", padx=(0, 8)
|
||||
)
|
||||
ttk.Button(asset_actions, text="Ausgewähltes Asset zurücknehmen", command=self._return_selected_asset).pack(
|
||||
side="left"
|
||||
ttk.Button(
|
||||
asset_actions,
|
||||
text="Ausgewähltes Asset zurücknehmen",
|
||||
command=self._return_selected_asset,
|
||||
).pack(
|
||||
side="left",
|
||||
)
|
||||
|
||||
documents_tab.columnconfigure(0, weight=1)
|
||||
@@ -493,7 +496,11 @@ class MemberTab(ttk.Frame):
|
||||
suffix = ""
|
||||
if key == self.claim_sort_column:
|
||||
suffix = " v" if self.claim_sort_descending else " ^"
|
||||
self.claims.heading(key, text=f"{title}{suffix}", command=lambda column=key: self._toggle_claim_sort(column))
|
||||
self.claims.heading(
|
||||
key,
|
||||
text=f"{title}{suffix}",
|
||||
command=lambda column=key: self._toggle_claim_sort(column),
|
||||
)
|
||||
|
||||
def _open_selected_claim(self) -> None:
|
||||
selected = self.claims.selection()
|
||||
|
||||
@@ -5,7 +5,6 @@ from collections.abc import Callable, Iterable
|
||||
from dataclasses import dataclass
|
||||
from tkinter import ttk
|
||||
|
||||
|
||||
MessageType = str
|
||||
|
||||
|
||||
|
||||
@@ -4,11 +4,16 @@ from collections.abc import Callable
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
from ccma.domain.dates import format_date_for_display
|
||||
from ccma.domain.models import ASSET_STATUS_LABELS, MEMBERSHIP_STATUS_LABELS, Asset, HousekeeperFinding, Member
|
||||
from ccma.domain.models import (
|
||||
ASSET_STATUS_LABELS,
|
||||
MEMBERSHIP_STATUS_LABELS,
|
||||
Asset,
|
||||
HousekeeperFinding,
|
||||
Member,
|
||||
)
|
||||
from ccma.ui.labels import storage_key
|
||||
from ccma.ui.sections import titled_frame
|
||||
|
||||
|
||||
MEMBER_TABLE_COLUMNS = (
|
||||
("number", "Nummer", 110),
|
||||
("first_name", "Vorname", 160),
|
||||
@@ -306,7 +311,11 @@ class MembersTab(ttk.Frame):
|
||||
suffix = ""
|
||||
if key == self.sort_column:
|
||||
suffix = " v" if self.sort_descending else " ^"
|
||||
self.tree.heading(key, text=f"{title}{suffix}", command=lambda column=key: self._toggle_sort(column))
|
||||
self.tree.heading(
|
||||
key,
|
||||
text=f"{title}{suffix}",
|
||||
command=lambda column=key: self._toggle_sort(column),
|
||||
)
|
||||
|
||||
def _open_selected(self) -> None:
|
||||
selected = self.tree.selection()
|
||||
@@ -368,7 +377,11 @@ class AssetsTab(ttk.Frame):
|
||||
)
|
||||
self.status_filter.grid(row=0, column=1, sticky="w")
|
||||
self.status_filter.bind("<<ComboboxSelected>>", lambda _event: self._render_assets())
|
||||
self.tree = ttk.Treeview(self, columns=tuple(key for key, _title, _width in ASSET_TABLE_COLUMNS), show="headings")
|
||||
self.tree = ttk.Treeview(
|
||||
self,
|
||||
columns=tuple(key for key, _title, _width in ASSET_TABLE_COLUMNS),
|
||||
show="headings",
|
||||
)
|
||||
for key, title, width in ASSET_TABLE_COLUMNS:
|
||||
self.tree.heading(key, text=title, command=lambda column=key: self._toggle_sort(column))
|
||||
self.tree.column(key, width=width, anchor="w")
|
||||
@@ -377,13 +390,28 @@ class AssetsTab(ttk.Frame):
|
||||
self.tree.bind("<<TreeviewSelect>>", lambda _event: self._update_actions())
|
||||
actions = ttk.Frame(self)
|
||||
actions.grid(row=3, column=0, sticky="e", pady=(10, 0))
|
||||
self.edit_button = ttk.Button(actions, text="Bearbeiten", command=self._edit_selected, state="disabled")
|
||||
self.edit_button = ttk.Button(
|
||||
actions,
|
||||
text="Bearbeiten",
|
||||
command=self._edit_selected,
|
||||
state="disabled",
|
||||
)
|
||||
self.edit_button.pack(side="left", padx=(0, 8))
|
||||
self.open_button = ttk.Button(actions, text="Öffnen", command=self._open_selected, state="disabled")
|
||||
self.open_button.pack(side="left", padx=(0, 8))
|
||||
self.issue_button = ttk.Button(actions, text="Ausgeben", command=self._issue_selected, state="disabled")
|
||||
self.issue_button = ttk.Button(
|
||||
actions,
|
||||
text="Ausgeben",
|
||||
command=self._issue_selected,
|
||||
state="disabled",
|
||||
)
|
||||
self.issue_button.pack(side="left", padx=(0, 8))
|
||||
self.return_button = ttk.Button(actions, text="Zurücknehmen", command=self._return_selected, state="disabled")
|
||||
self.return_button = ttk.Button(
|
||||
actions,
|
||||
text="Zurücknehmen",
|
||||
command=self._return_selected,
|
||||
state="disabled",
|
||||
)
|
||||
self.return_button.pack(side="left")
|
||||
self.refresh(self.assets)
|
||||
|
||||
@@ -437,7 +465,11 @@ class AssetsTab(ttk.Frame):
|
||||
suffix = ""
|
||||
if key == self.sort_column:
|
||||
suffix = " v" if self.sort_descending else " ^"
|
||||
self.tree.heading(key, text=f"{title}{suffix}", command=lambda column=key: self._toggle_sort(column))
|
||||
self.tree.heading(
|
||||
key,
|
||||
text=f"{title}{suffix}",
|
||||
command=lambda column=key: self._toggle_sort(column),
|
||||
)
|
||||
|
||||
def _selected_asset_id(self) -> str:
|
||||
selected = self.tree.selection()
|
||||
|
||||
@@ -422,4 +422,7 @@ def test_housekeeper_reports_json_hash_mismatch(tmp_path) -> None:
|
||||
|
||||
findings = Housekeeper(repository).run()
|
||||
|
||||
assert any(finding.code == "json_hash_mismatch" and finding.member_id == member.member_id for finding in findings)
|
||||
assert any(
|
||||
finding.code == "json_hash_mismatch" and finding.member_id == member.member_id
|
||||
for finding in findings
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user