import sqlite3 from tkinter import messagebox import customtkinter as ctk from app.ui.base_frame import BaseAppFrame from app.utils.validators import normalize_text, parse_float class AccountsFrame(BaseAppFrame): """Screen for account management.""" def __init__(self, master, refresh_callback) -> None: super().__init__(master, refresh_callback) self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(0, weight=1) self._build_form() self._build_list() def _build_form(self) -> None: form_frame = ctk.CTkFrame(self, corner_radius=20) form_frame.grid(row=0, column=0, sticky="ns", padx=(8, 12), pady=8) self.title_label(form_frame, "Счета и карты").pack( anchor="w", padx=18, pady=(18, 4) ) self.subtitle_label( form_frame, "Добавьте карту, кошелёк, счёт или наличные.", ).pack(anchor="w", padx=18, pady=(0, 18)) self.name_entry = ctk.CTkEntry( form_frame, width=300, placeholder_text="Название счёта", ) self.name_entry.pack(padx=18, pady=8) self.type_menu = ctk.CTkOptionMenu( form_frame, width=300, values=["Карта", "Наличные", "Счёт", "Электронный кошелёк"], ) self.type_menu.set("Карта") self.type_menu.pack(padx=18, pady=8) self.balance_entry = ctk.CTkEntry( form_frame, width=300, placeholder_text="Стартовый баланс", ) self.balance_entry.pack(padx=18, pady=8) self.add_button = self.primary_button( form_frame, "Добавить счёт", lambda: self.run_safe(self.add_account, self.add_button), ) self.add_button.pack(fill="x", padx=18, pady=(12, 18)) def _build_list(self) -> None: self.list_frame = ctk.CTkScrollableFrame(self, label_text="Список счетов") self.list_frame.grid(row=0, column=1, sticky="nsew", padx=(0, 8), pady=8) def add_account(self) -> None: name = normalize_text(self.name_entry.get(), "Название") account_type = self.type_menu.get() balance = parse_float(self.balance_entry.get(), "Стартовый баланс") self.service.add_account(name, account_type, balance) self.name_entry.delete(0, "end") self.balance_entry.delete(0, "end") self.refresh_callback() def delete_account(self, account_id: int) -> None: if not messagebox.askyesno("Подтверждение", "Удалить выбранный счёт?"): return try: self.service.delete_account(account_id) except sqlite3.IntegrityError: messagebox.showerror( "Ошибка", "Нельзя удалить счёт, пока к нему привязаны операции.", ) return self.refresh_callback() def refresh_data(self) -> None: self.clear(self.list_frame) rows = self.service.list_accounts() if not rows: self._empty("Пока нет ни одного счёта.") return for row in rows: card = ctk.CTkFrame(self.list_frame, corner_radius=16) card.pack(fill="x", padx=6, pady=6) card.grid_columnconfigure(0, weight=1) ctk.CTkLabel( card, text=row["name"], font=ctk.CTkFont(size=18, weight="bold"), ).grid(row=0, column=0, sticky="w", padx=16, pady=(12, 4)) button = self.danger_button( card, "Удалить", lambda account_id=row["id"]: self.run_safe( lambda: self.delete_account(account_id) ), ) button.grid(row=0, column=1, sticky="e", padx=16, pady=(12, 4)) ctk.CTkLabel( card, text=f"Тип: {row['account_type']} | Баланс: {self.money(row['balance'])}", text_color="gray70", ).grid(row=1, column=0, columnspan=2, sticky="w", padx=16, pady=(0, 14)) def _empty(self, text: str) -> None: ctk.CTkLabel(self.list_frame, text=text, text_color="gray70").pack( anchor="w", padx=10, pady=10 )