192 lines
6.3 KiB
Python
192 lines
6.3 KiB
Python
from tkinter import messagebox
|
|
|
|
import customtkinter as ctk
|
|
|
|
from app.database import get_connection
|
|
|
|
|
|
class SubscriptionsFrame(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=300,
|
|
placeholder_text="Название",
|
|
)
|
|
self.name_entry.pack(padx=18, pady=8)
|
|
|
|
self.amount_entry = ctk.CTkEntry(
|
|
form_frame,
|
|
width=300,
|
|
placeholder_text="Стоимость",
|
|
)
|
|
self.amount_entry.pack(padx=18, pady=8)
|
|
|
|
self.day_entry = ctk.CTkEntry(
|
|
form_frame,
|
|
width=300,
|
|
placeholder_text="День списания (1-31)",
|
|
)
|
|
self.day_entry.pack(padx=18, pady=8)
|
|
|
|
self.period_menu = ctk.CTkOptionMenu(
|
|
form_frame,
|
|
width=300,
|
|
values=["Месяц", "Год"],
|
|
)
|
|
self.period_menu.set("Месяц")
|
|
self.period_menu.pack(padx=18, pady=8)
|
|
|
|
self.status_menu = ctk.CTkOptionMenu(
|
|
form_frame,
|
|
width=300,
|
|
values=["Активна", "Пауза"],
|
|
)
|
|
self.status_menu.set("Активна")
|
|
self.status_menu.pack(padx=18, pady=8)
|
|
|
|
add_button = ctk.CTkButton(
|
|
form_frame,
|
|
text="Добавить подписку",
|
|
width=300,
|
|
height=42,
|
|
command=self.add_subscription,
|
|
)
|
|
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_subscription(self) -> None:
|
|
name = self.name_entry.get().strip()
|
|
amount_text = self.amount_entry.get().strip().replace(",", ".")
|
|
billing_day_text = self.day_entry.get().strip()
|
|
period = self.period_menu.get()
|
|
status = self.status_menu.get()
|
|
|
|
if not name or not amount_text or not billing_day_text:
|
|
messagebox.showerror("Ошибка", "Заполните все поля.")
|
|
return
|
|
|
|
try:
|
|
amount = float(amount_text)
|
|
billing_day = int(billing_day_text)
|
|
except ValueError:
|
|
messagebox.showerror(
|
|
"Ошибка",
|
|
"Стоимость и день списания должны быть числами.",
|
|
)
|
|
return
|
|
|
|
if amount <= 0 or not 1 <= billing_day <= 31:
|
|
messagebox.showerror("Ошибка", "Проверьте стоимость и день списания.")
|
|
return
|
|
|
|
with get_connection() as connection:
|
|
connection.execute(
|
|
"""
|
|
INSERT INTO subscriptions (name, amount, billing_day, period, status)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
""",
|
|
(name, amount, billing_day, period, status),
|
|
)
|
|
connection.commit()
|
|
|
|
self.name_entry.delete(0, "end")
|
|
self.amount_entry.delete(0, "end")
|
|
self.day_entry.delete(0, "end")
|
|
self.refresh_callback()
|
|
|
|
def delete_subscription(self, subscription_id: int) -> None:
|
|
with get_connection() as connection:
|
|
connection.execute(
|
|
"DELETE FROM subscriptions WHERE id = ?",
|
|
(subscription_id,),
|
|
)
|
|
connection.commit()
|
|
|
|
self.refresh_callback()
|
|
|
|
def refresh_data(self) -> None:
|
|
for widget in self.list_frame.winfo_children():
|
|
widget.destroy()
|
|
|
|
with get_connection() as connection:
|
|
rows = connection.execute(
|
|
"SELECT * FROM subscriptions ORDER BY id DESC"
|
|
).fetchall()
|
|
|
|
if not rows:
|
|
empty_label = ctk.CTkLabel(
|
|
self.list_frame,
|
|
text="Подписок пока нет.",
|
|
text_color="gray70",
|
|
)
|
|
empty_label.pack(anchor="w", padx=10, pady=10)
|
|
return
|
|
|
|
for row in rows:
|
|
card = ctk.CTkFrame(self.list_frame, corner_radius=16)
|
|
card.pack(fill="x", padx=6, pady=6)
|
|
|
|
title = ctk.CTkLabel(
|
|
card,
|
|
text=row["name"],
|
|
font=ctk.CTkFont(size=18, weight="bold"),
|
|
)
|
|
title.grid(row=0, column=0, sticky="w", padx=16, pady=(12, 4))
|
|
|
|
delete_button = ctk.CTkButton(
|
|
card,
|
|
text="Удалить",
|
|
width=90,
|
|
fg_color="#b33939",
|
|
hover_color="#932f2f",
|
|
command=lambda subscription_id=row["id"]: self.delete_subscription(
|
|
subscription_id
|
|
),
|
|
)
|
|
delete_button.grid(row=0, column=1, sticky="e", padx=16, pady=(12, 4))
|
|
|
|
monthly_equivalent = (
|
|
row["amount"] / 12 if row["period"] == "Год" else row["amount"]
|
|
)
|
|
|
|
info = ctk.CTkLabel(
|
|
card,
|
|
text=(
|
|
f"{row['amount']:,.2f} ₽ | {row['period']} | "
|
|
f"списание: {row['billing_day']} числа | {row['status']} | "
|
|
f"≈ {monthly_equivalent:,.2f} ₽ в месяц"
|
|
).replace(",", " "),
|
|
text_color="gray75",
|
|
)
|
|
info.grid(row=1, column=0, columnspan=2, sticky="w", padx=16, pady=(0, 14))
|