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))