Загрузка данных


# main.py
import tkinter as tk
from tkinter import ttk, messagebox
from database import DatabaseManager
from datetime import datetime

class OrderApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Анализ заказов")
        self.root.geometry("1000x700")
        self.root.resizable(True, True)
        
        # Подключение к БД
        self.db = DatabaseManager()
        try:
            self.db.connect()
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось подключиться к БД: {e}")
            self.root.quit()
        
        # Данные
        self.all_orders = []
        self.filtered_orders = []
        self.customers = []
        self.customers_dict = {}
        
        # Заголовок
        title = tk.Label(root, text="Работа с заказами", font=("Arial", 18, "bold"))
        title.pack(pady=10)
        
        # Верхняя панель с фильтрацией
        self.create_filter_panel()
        
        # Панель поиска
        self.create_search_panel()
        
        # Панель сортировки
        self.create_sort_panel()
        
        # Таблица для отображения данных
        self.create_table()
        
        # Нижняя панель с итогами
        self.create_bottom_panel()
        
        # Загрузка данных
        self.load_initial_data()
    
    def create_filter_panel(self):
        """Создание панели фильтрации по клиенту"""
        filter_frame = ttk.LabelFrame(self.root, text="Фильтр по заказчику", padding="10")
        filter_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(filter_frame, text="Выберите заказчика:").grid(row=0, column=0, padx=5, pady=5)
        
        self.customer_var = tk.StringVar()
        self.customer_combo = ttk.Combobox(filter_frame, textvariable=self.customer_var, width=40)
        self.customer_combo.grid(row=0, column=1, padx=5, pady=5)
        
        ttk.Button(filter_frame, text="Фильтровать", command=self.filter_by_customer).grid(row=0, column=2, padx=5)
        ttk.Button(filter_frame, text="Показать все", command=self.show_all).grid(row=0, column=3, padx=5)
    
    def create_search_panel(self):
        """Создание панели поиска"""
        search_frame = ttk.LabelFrame(self.root, text="Поиск", padding="10")
        search_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(search_frame, text="Введите строку поиска:").grid(row=0, column=0, padx=5, pady=5)
        
        self.search_var = tk.StringVar()
        self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=40)
        self.search_entry.grid(row=0, column=1, padx=5, pady=5)
        
        ttk.Button(search_frame, text="Найти", command=self.search_text).grid(row=0, column=2, padx=5)
    
    def create_sort_panel(self):
        """Создание панели сортировки"""
        sort_frame = ttk.LabelFrame(self.root, text="Сортировка", padding="10")
        sort_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(sort_frame, text="Выберите поле для сортировки:").grid(row=0, column=0, padx=5, pady=5)
        
        self.sort_field = tk.StringVar(value="Заказчик")
        sort_combo = ttk.Combobox(sort_frame, textvariable=self.sort_field, 
                                  values=["Заказчик", "Дата заказа", "Сумма заказа"], 
                                  state="readonly", width=20)
        sort_combo.grid(row=0, column=1, padx=5, pady=5)
        
        # Переключатели для направления сортировки
        self.sort_order = tk.StringVar(value="asc")
        ttk.Radiobutton(sort_frame, text="По возрастанию", variable=self.sort_order, 
                       value="asc", command=self.apply_sort).grid(row=0, column=2, padx=10)
        ttk.Radiobutton(sort_frame, text="По убыванию", variable=self.sort_order, 
                       value="desc", command=self.apply_sort).grid(row=0, column=3, padx=10)
        
        ttk.Button(sort_frame, text="Применить сортировку", command=self.apply_sort).grid(row=0, column=4, padx=10)
    
    def create_table(self):
        """Создание таблицы для отображения данных"""
        table_frame = ttk.Frame(self.root)
        table_frame.pack(expand=True, fill=tk.BOTH, padx=10, pady=5)
        
        # Скроллинг
        scrollbar = ttk.Scrollbar(table_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # Treeview
        self.tree = ttk.Treeview(table_frame, 
                                 columns=("customer", "city", "phone", "date", "sum"),
                                 show="headings",
                                 yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.tree.yview)
        
        # Заголовки
        self.tree.heading("customer", text="Заказчик", command=lambda: self.sort_by_click("Заказчик"))
        self.tree.heading("city", text="Город")
        self.tree.heading("phone", text="Телефон")
        self.tree.heading("date", text="Дата заказа", command=lambda: self.sort_by_click("Дата заказа"))
        self.tree.heading("sum", text="Сумма заказа", command=lambda: self.sort_by_click("Сумма заказа"))
        
        # Ширина колонок
        self.tree.column("customer", width=250)
        self.tree.column("city", width=150)
        self.tree.column("phone", width=150)
        self.tree.column("date", width=120)
        self.tree.column("sum", width=120, anchor=tk.E)
        
        self.tree.pack(expand=True, fill=tk.BOTH)
        
        # Теги для выделения поиска
        self.tree.tag_configure('found', background='yellow')
    
    def create_bottom_panel(self):
        """Создание нижней панели с итогами"""
        bottom_frame = ttk.Frame(self.root)
        bottom_frame.pack(fill=tk.X, padx=10, pady=10)
        
        self.total_orders_label = ttk.Label(bottom_frame, text="Всего заказов: 0", 
                                           font=("Arial", 11, "bold"))
        self.total_orders_label.pack(side=tk.LEFT, padx=20)
        
        self.total_sum_label = ttk.Label(bottom_frame, text="Общая сумма: 0.00", 
                                        font=("Arial", 11, "bold"))
        self.total_sum_label.pack(side=tk.LEFT, padx=20)
        
        self.status_label = ttk.Label(bottom_frame, text="Готово", font=("Arial", 10))
        self.status_label.pack(side=tk.RIGHT, padx=20)
    
    def load_initial_data(self):
        """Загрузка начальных данных"""
        try:
            # Загрузка клиентов
            self.customers = self.db.get_customers()
            self.customers_dict = {name: id for id, name in self.customers}
            self.customer_combo['values'] = list(self.customers_dict.keys())
            
            # Загрузка заказов
            self.all_orders = self.db.get_all_orders()
            self.filtered_orders = self.all_orders.copy()
            
            self.display_data()
            self.status_label.config(text=f"Загружено заказов: {len(self.all_orders)}")
            
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось загрузить данные: {e}")
    
    def display_data(self):
        """Отображение данных в таблице"""
        # Очистка таблицы
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        # Вставка данных
        for order in self.filtered_orders:
            # Извлечение города из адреса
            city = self.extract_city(order.get('CITY', ''))
            
            # Форматирование даты
            date = order['ORDER_DATE']
            if hasattr(date, 'strftime'):
                date_str = date.strftime('%d.%m.%Y')
            else:
                date_str = str(date)
            
            self.tree.insert("", tk.END, values=(
                order['CUSTOMER_NAME'],
                city,
                order['PHONE'],
                date_str,
                f"{order['ORDER_SUM']:.2f}"
            ))
        
        # Обновление итогов
        self.update_totals()
    
    def extract_city(self, address):
        """Извлечение города из адреса"""
        if not address:
            return ""
        # Пытаемся найти "г. "
        parts = address.split("г. ")
        if len(parts) > 1:
            city = parts[1].split(",")[0]
            return city
        return address[:30] if address else ""
    
    def update_totals(self):
        """Обновление итоговых значений"""
        total_orders = len(self.filtered_orders)
        total_sum = sum(order['ORDER_SUM'] for order in self.filtered_orders)
        
        self.total_orders_label.config(text=f"Всего заказов: {total_orders}")
        self.total_sum_label.config(text=f"Общая сумма: {total_sum:,.2f}".replace(',', ' '))
    
    def filter_by_customer(self):
        """Фильтрация по выбранному клиенту"""
        customer_name = self.customer_var.get()
        if not customer_name:
            messagebox.showwarning("Предупреждение", "Выберите заказчика")
            return
        
        try:
            customer_id = self.customers_dict.get(customer_name)
            if customer_id:
                self.filtered_orders = self.db.get_orders_by_customer(customer_id)
                self.display_data()
                self.status_label.config(text=f"Показаны заказы клиента: {customer_name}")
        except Exception as e:
            messagebox.showerror("Ошибка", f"Ошибка фильтрации: {e}")
    
    def show_all(self):
        """Показать все заказы"""
        self.customer_var.set("")
        self.filtered_orders = self.all_orders.copy()
        self.display_data()
        self.status_label.config(text="Показаны все заказы")
    
    def search_text(self):
        """Поиск и выделение текста"""
        search_str = self.search_var.get().lower()
        if not search_str:
            # Сброс выделения
            for item in self.tree.get_children():
                self.tree.item(item, tags=())
            return
        
        # Поиск и выделение
        found_count = 0
        for item in self.tree.get_children():
            values = self.tree.item(item)['values']
            found = False
            for value in values:
                if search_str in str(value).lower():
                    found = True
                    found_count += 1
            
            if found:
                self.tree.item(item, tags=('found',))
            else:
                self.tree.item(item, tags=())
        
        self.status_label.config(text=f"Найдено совпадений: {found_count}")
    
    def apply_sort(self):
        """Применение сортировки"""
        field = self.sort_field.get()
        order = self.sort_order.get()
        
        self.sort_data(field, order)
    
    def sort_by_click(self, field):
        """Сортировка по клику на заголовок"""
        # При клике переключаем направление
        if self.sort_field.get() == field:
            # Меняем направление
            self.sort_order.set("desc" if self.sort_order.get() == "asc" else "asc")
        else:
            # Новое поле сортировки
            self.sort_field.set(field)
            self.sort_order.set("asc")
        
        self.sort_data(field, self.sort_order.get())
    
    def sort_data(self, field, order):
        """Сортировка данных"""
        try:
            if field == "Заказчик":
                self.filtered_orders.sort(key=lambda x: x['CUSTOMER_NAME'], 
                                         reverse=(order == "desc"))
            elif field == "Дата заказа":
                self.filtered_orders.sort(key=lambda x: x['ORDER_DATE'], 
                                         reverse=(order == "desc"))
            elif field == "Сумма заказа":
                self.filtered_orders.sort(key=lambda x: x['ORDER_SUM'], 
                                         reverse=(order == "desc"))
            
            self.display_data()
            self.status_label.config(text=f"Отсортировано по {field} ({order})")
            
        except Exception as e:
            messagebox.showerror("Ошибка", f"Ошибка сортировки: {e}")
    
    def __del__(self):
        """Деструктор - закрытие соединения с БД"""
        try:
            self.db.disconnect()
        except:
            pass

if __name__ == "__main__":
    root = tk.Tk()
    app = OrderApp(root)
    root.mainloop()