Добавлены каркас приложения, база данных, счета, категории и операции
This commit is contained in:
163
app/ui/accounts_frame.py
Normal file
163
app/ui/accounts_frame.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import sqlite3
|
||||
from tkinter import messagebox
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from app.database import get_connection
|
||||
|
||||
|
||||
class AccountsFrame(ctk.CTkFrame):
|
||||
def __init__(self, master, refresh_callback) -> None:
|
||||
super().__init__(master)
|
||||
self.refresh_callback = 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=18)
|
||||
form_frame.grid(row=0, column=0, sticky="ns", padx=(8, 12), pady=8)
|
||||
|
||||
title = ctk.CTkLabel(
|
||||
form_frame,
|
||||
text="Счета и карты",
|
||||
font=ctk.CTkFont(size=26, weight="bold"),
|
||||
)
|
||||
title.pack(anchor="w", padx=18, pady=(18, 6))
|
||||
|
||||
subtitle = ctk.CTkLabel(
|
||||
form_frame,
|
||||
text="Добавьте карту, кошелёк или наличные.",
|
||||
text_color="gray75",
|
||||
)
|
||||
subtitle.pack(anchor="w", padx=18, pady=(0, 18))
|
||||
|
||||
self.name_entry = ctk.CTkEntry(
|
||||
form_frame,
|
||||
width=280,
|
||||
placeholder_text="Название",
|
||||
)
|
||||
self.name_entry.pack(padx=18, pady=8)
|
||||
|
||||
self.type_menu = ctk.CTkOptionMenu(
|
||||
form_frame,
|
||||
width=280,
|
||||
values=["Карта", "Наличные", "Счёт", "Электронный кошелёк"],
|
||||
)
|
||||
self.type_menu.pack(padx=18, pady=8)
|
||||
self.type_menu.set("Карта")
|
||||
|
||||
self.balance_entry = ctk.CTkEntry(
|
||||
form_frame,
|
||||
width=280,
|
||||
placeholder_text="Стартовый баланс",
|
||||
)
|
||||
self.balance_entry.pack(padx=18, pady=8)
|
||||
|
||||
add_button = ctk.CTkButton(
|
||||
form_frame,
|
||||
text="Добавить счёт",
|
||||
width=280,
|
||||
height=42,
|
||||
command=self.add_account,
|
||||
)
|
||||
add_button.pack(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 = self.name_entry.get().strip()
|
||||
account_type = self.type_menu.get()
|
||||
balance_text = self.balance_entry.get().strip().replace(",", ".")
|
||||
|
||||
if not name or not balance_text:
|
||||
messagebox.showerror("Ошибка", "Заполните все поля.")
|
||||
return
|
||||
|
||||
try:
|
||||
balance = float(balance_text)
|
||||
except ValueError:
|
||||
messagebox.showerror("Ошибка", "Баланс должен быть числом.")
|
||||
return
|
||||
|
||||
with get_connection() as connection:
|
||||
connection.execute(
|
||||
"""
|
||||
INSERT INTO accounts (name, account_type, balance)
|
||||
VALUES (?, ?, ?)
|
||||
""",
|
||||
(name, account_type, balance),
|
||||
)
|
||||
connection.commit()
|
||||
|
||||
self.name_entry.delete(0, "end")
|
||||
self.balance_entry.delete(0, "end")
|
||||
self.refresh_callback()
|
||||
|
||||
def delete_account(self, account_id: int) -> None:
|
||||
try:
|
||||
with get_connection() as connection:
|
||||
connection.execute("DELETE FROM accounts WHERE id = ?", (account_id,))
|
||||
connection.commit()
|
||||
except sqlite3.IntegrityError:
|
||||
messagebox.showerror(
|
||||
"Ошибка",
|
||||
"Нельзя удалить счёт, пока к нему привязаны операции.",
|
||||
)
|
||||
return
|
||||
|
||||
self.refresh_callback()
|
||||
|
||||
def refresh_data(self) -> None:
|
||||
for widget in self.list_frame.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
with get_connection() as connection:
|
||||
accounts = connection.execute(
|
||||
"SELECT * FROM accounts ORDER BY id DESC"
|
||||
).fetchall()
|
||||
|
||||
if not accounts:
|
||||
empty_label = ctk.CTkLabel(
|
||||
self.list_frame,
|
||||
text="Пока нет ни одного счёта.",
|
||||
text_color="gray70",
|
||||
)
|
||||
empty_label.pack(anchor="w", padx=10, pady=10)
|
||||
return
|
||||
|
||||
for account in accounts:
|
||||
card = ctk.CTkFrame(self.list_frame, corner_radius=16)
|
||||
card.pack(fill="x", padx=6, pady=6)
|
||||
|
||||
title = ctk.CTkLabel(
|
||||
card,
|
||||
text=account["name"],
|
||||
font=ctk.CTkFont(size=18, weight="bold"),
|
||||
)
|
||||
title.grid(row=0, column=0, sticky="w", padx=16, pady=(12, 6))
|
||||
|
||||
delete_button = ctk.CTkButton(
|
||||
card,
|
||||
text="Удалить",
|
||||
width=90,
|
||||
fg_color="#b33939",
|
||||
hover_color="#932f2f",
|
||||
command=lambda account_id=account["id"]: self.delete_account(account_id),
|
||||
)
|
||||
delete_button.grid(row=0, column=1, sticky="e", padx=16, pady=(12, 6))
|
||||
|
||||
info = ctk.CTkLabel(
|
||||
card,
|
||||
text=(
|
||||
f"Тип: {account['account_type']} | Баланс: "
|
||||
f"{account['balance']:,.2f} ₽"
|
||||
).replace(",", " "),
|
||||
text_color="gray75",
|
||||
)
|
||||
info.grid(row=1, column=0, columnspan=2, sticky="w", padx=16, pady=(0, 14))
|
||||
Reference in New Issue
Block a user