| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 |
- import psycopg2
- import tkinter as tk
- from tkinter import ttk, messagebox, simpledialog
- from datetime import datetime
- import csv
- # ========== НАСТРОЙКИ БД ==========
- DB_CONFIG = {
- "dbname": "dbwork",
- "user": "zhelezkov_fv",
- "password": "0&6&I#u#s#B",
- "host": "edu-pg.itiscaf.ru",
- "port": 5432
- }
- def get_conn():
- return psycopg2.connect(**DB_CONFIG)
- # ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ДЛЯ СПРАВОЧНИКОВ ==========
- def get_categories():
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("SELECT id, name FROM Category ORDER BY name")
- return cur.fetchall()
- def get_equipment_list():
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("SELECT id, name FROM Equipment ORDER BY name")
- return cur.fetchall()
- def get_employees():
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("SELECT id, full_name FROM Employee ORDER BY full_name")
- return cur.fetchall()
- def get_suppliers():
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("SELECT id, address FROM Supplier ORDER BY address")
- return cur.fetchall()
- def get_warehouses():
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("SELECT id, address FROM Warehouse ORDER BY address")
- return cur.fetchall()
- # ========== ОБНОВЛЕНИЕ ТАБЛИЦ ==========
- def refresh_stock(tree):
- for row in tree.get_children():
- tree.delete(row)
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- SELECT id, name, manufacturer, category, current_quantity
- FROM EquipmentStock
- ORDER BY name
- """)
- for row in cur.fetchall():
- tree.insert("", tk.END, values=row)
- def refresh_purchases(tree):
- for row in tree.get_children():
- tree.delete(row)
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- SELECT id, equipment, supplier, warehouse, quantity,
- price_per_unit, total_cost, purchase_date, notes
- FROM PurchaseDetails
- ORDER BY purchase_date DESC
- """)
- for row in cur.fetchall():
- tree.insert("", tk.END, values=row)
- def refresh_writeoffs(tree):
- for row in tree.get_children():
- tree.delete(row)
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- SELECT id, equipment, employee, quantity, reason, write_off_date
- FROM WriteOffDetails
- ORDER BY write_off_date DESC
- """)
- for row in cur.fetchall():
- tree.insert("", tk.END, values=row)
- # ========== ПОИСК ==========
- def search_stock(tree, search_entry):
- keyword = search_entry.get().strip()
- for row in tree.get_children():
- tree.delete(row)
- if not keyword:
- refresh_stock(tree)
- return
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- SELECT e.id, e.name, e.manufacturer, c.name AS category,
- COALESCE(SUM(p.quantity), 0) - COALESCE(SUM(w.quantity), 0) AS current_quantity
- FROM Equipment e
- LEFT JOIN Category c ON e.category_id = c.id
- LEFT JOIN Purchase p ON e.id = p.equipment_id
- LEFT JOIN WriteOff w ON e.id = w.equipment_id
- WHERE e.name ILIKE %s OR e.manufacturer ILIKE %s
- GROUP BY e.id, c.name
- ORDER BY e.name
- """, (f'{keyword}%', f'{keyword}%')) # изменено: только % в конце
- for row in cur.fetchall():
- tree.insert("", tk.END, values=row)
- # ========== CRUD ДЛЯ ЗАКУПОК ==========
- def add_purchase(parent, refresh_callback):
- win = tk.Toplevel(parent)
- win.title("Новая закупка")
- win.geometry("500x400")
- win.grab_set()
- # Поля
- tk.Label(win, text="Оборудование:").grid(row=0, column=0, sticky="e", padx=5, pady=5)
- equip_var = tk.StringVar()
- equip_combo = ttk.Combobox(win, textvariable=equip_var, values=[f"{e[0]} - {e[1]}" for e in get_equipment_list()], width=40)
- equip_combo.grid(row=0, column=1, padx=5, pady=5)
- tk.Label(win, text="Поставщик:").grid(row=1, column=0, sticky="e")
- supp_var = tk.StringVar()
- supp_combo = ttk.Combobox(win, textvariable=supp_var, values=[f"{s[0]} - {s[1]}" for s in get_suppliers()], width=40)
- supp_combo.grid(row=1, column=1, padx=5, pady=5)
- tk.Label(win, text="Склад:").grid(row=2, column=0, sticky="e")
- wh_var = tk.StringVar()
- wh_combo = ttk.Combobox(win, textvariable=wh_var, values=[f"{w[0]} - {w[1]}" for w in get_warehouses()], width=40)
- wh_combo.grid(row=2, column=1, padx=5, pady=5)
- tk.Label(win, text="Количество:").grid(row=3, column=0, sticky="e")
- qty_entry = tk.Entry(win)
- qty_entry.grid(row=3, column=1, padx=5, pady=5)
- tk.Label(win, text="Цена за ед. (руб):").grid(row=4, column=0, sticky="e")
- price_entry = tk.Entry(win)
- price_entry.grid(row=4, column=1, padx=5, pady=5)
- tk.Label(win, text="Дата закупки (ГГГГ-ММ-ДД):").grid(row=5, column=0, sticky="e")
- date_entry = tk.Entry(win)
- date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))
- date_entry.grid(row=5, column=1, padx=5, pady=5)
- tk.Label(win, text="Примечание:").grid(row=6, column=0, sticky="ne")
- notes_text = tk.Text(win, height=4, width=30)
- notes_text.grid(row=6, column=1, padx=5, pady=5)
- def save():
- if not equip_var.get() or not supp_var.get() or not wh_var.get():
- messagebox.showerror("Ошибка", "Заполните все обязательные поля")
- return
- try:
- eq_id = int(equip_var.get().split(" - ")[0])
- sup_id = int(supp_var.get().split(" - ")[0])
- wh_id = int(wh_var.get().split(" - ")[0])
- qty = int(qty_entry.get())
- price = float(price_entry.get())
- date = date_entry.get()
- datetime.strptime(date, "%Y-%m-%d")
- notes = notes_text.get("1.0", tk.END).strip()
- except Exception:
- messagebox.showerror("Ошибка", "Проверьте введённые данные (количество – целое, цена – число, дата в формате ГГГГ-ММ-ДД)")
- return
- try:
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- INSERT INTO Purchase (equipment_id, supplier_id, warehouse_id, quantity, price_per_unit, purchase_date, notes)
- VALUES (%s, %s, %s, %s, %s, %s, %s)
- """, (eq_id, sup_id, wh_id, qty, price, date, notes))
- conn.commit()
- messagebox.showinfo("Успех", "Закупка добавлена")
- win.destroy()
- refresh_callback()
- except Exception as e:
- messagebox.showerror("Ошибка БД", str(e))
- tk.Button(win, text="Сохранить", command=save, bg="lightgreen").grid(row=7, column=0, columnspan=2, pady=15)
- def delete_purchase(tree, refresh_callback):
- selected = tree.selection()
- if not selected:
- messagebox.showwarning("Выбор", "Выберите запись для удаления")
- return
- item = tree.item(selected[0])
- purchase_id = item['values'][0]
- if messagebox.askyesno("Удаление", f"Удалить закупку ID {purchase_id}?"):
- try:
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("DELETE FROM Purchase WHERE id = %s", (purchase_id,))
- conn.commit()
- refresh_callback()
- except Exception as e:
- messagebox.showerror("Ошибка", str(e))
- def edit_purchase(tree, refresh_callback):
- selected = tree.selection()
- if not selected:
- messagebox.showwarning("Выбор", "Выберите закупку для редактирования")
- return
- item = tree.item(selected[0])
- purchase_id = item['values'][0]
- # Получаем текущие данные
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("SELECT equipment_id, supplier_id, warehouse_id, quantity, price_per_unit, purchase_date, notes FROM Purchase WHERE id = %s", (purchase_id,))
- data = cur.fetchone()
- if not data:
- return
- win = tk.Toplevel(tree)
- win.title("Редактирование закупки")
- win.geometry("500x400")
- win.grab_set()
- # Заполняем поля (аналогично add, но с предзаполнением)
- # ... (для краткости покажу принцип, полный код аналогичен add)
- # Для экономии места, в рабочей версии нужно реализовать, но здесь ограничение символов.
- # Я дам полный код в конце ответа отдельным блоком.
- messagebox.showinfo("Уведомление", "Функция редактирования аналогична добавлению. Полный код предоставлен в итоговом приложении.")
- # ========== CRUD ДЛЯ СПИСАНИЙ ==========
- def add_writeoff(parent, refresh_callback):
- win = tk.Toplevel(parent)
- win.title("Новое списание")
- win.geometry("450x350")
- win.grab_set()
- tk.Label(win, text="Оборудование:").grid(row=0, column=0, sticky="e")
- equip_var = tk.StringVar()
- equip_combo = ttk.Combobox(win, textvariable=equip_var, values=[f"{e[0]} - {e[1]}" for e in get_equipment_list()], width=40)
- equip_combo.grid(row=0, column=1, padx=5, pady=5)
- tk.Label(win, text="Сотрудник:").grid(row=1, column=0, sticky="e")
- emp_var = tk.StringVar()
- emp_combo = ttk.Combobox(win, textvariable=emp_var, values=[f"{e[0]} - {e[1]}" for e in get_employees()], width=40)
- emp_combo.grid(row=1, column=1, padx=5, pady=5)
- tk.Label(win, text="Количество:").grid(row=2, column=0, sticky="e")
- qty_entry = tk.Entry(win)
- qty_entry.grid(row=2, column=1, padx=5, pady=5)
- tk.Label(win, text="Причина списания:").grid(row=3, column=0, sticky="ne")
- reason_text = tk.Text(win, height=3, width=30)
- reason_text.grid(row=3, column=1, padx=5, pady=5)
- tk.Label(win, text="Дата списания (ГГГГ-ММ-ДД):").grid(row=4, column=0, sticky="e")
- date_entry = tk.Entry(win)
- date_entry.insert(0, datetime.now().strftime("%Y-%m-%d"))
- date_entry.grid(row=4, column=1, padx=5, pady=5)
- def save():
- if not equip_var.get() or not emp_var.get():
- messagebox.showerror("Ошибка", "Заполните оборудование и сотрудника")
- return
- try:
- eq_id = int(equip_var.get().split(" - ")[0])
- emp_id = int(emp_var.get().split(" - ")[0])
- qty = int(qty_entry.get())
- reason = reason_text.get("1.0", tk.END).strip()
- date = date_entry.get()
- datetime.strptime(date, "%Y-%m-%d")
- except Exception:
- messagebox.showerror("Ошибка", "Неверные данные")
- return
- try:
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- INSERT INTO WriteOff (equipment_id, employee_id, quantity, reason, write_off_date)
- VALUES (%s, %s, %s, %s, %s)
- """, (eq_id, emp_id, qty, reason, date))
- conn.commit()
- messagebox.showinfo("Успех", "Списание добавлено")
- win.destroy()
- refresh_callback()
- except Exception as e:
- messagebox.showerror("Ошибка", str(e))
- tk.Button(win, text="Сохранить", command=save, bg="lightcoral").grid(row=5, column=0, columnspan=2, pady=15)
- def delete_writeoff(tree, refresh_callback):
- selected = tree.selection()
- if not selected:
- messagebox.showwarning("Выбор", "Выберите списание для удаления")
- return
- item = tree.item(selected[0])
- wo_id = item['values'][0]
- if messagebox.askyesno("Удаление", f"Удалить списание ID {wo_id}?"):
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("DELETE FROM WriteOff WHERE id = %s", (wo_id,))
- conn.commit()
- refresh_callback()
- # ========== ЭКСПОРТ ==========
- def export_to_csv(tree, filename_prefix):
- data = []
- for child in tree.get_children():
- vals = tree.item(child)['values']
- data.append(vals)
- if not data:
- messagebox.showwarning("Нет данных", "Нечего экспортировать")
- return
- filename = f"{filename_prefix}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
- with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
- writer = csv.writer(f)
- cols = [tree.heading(c)['text'] for c in tree['columns']]
- writer.writerow(cols)
- writer.writerows(data)
- messagebox.showinfo("Экспорт", f"Сохранён файл {filename}")
- # ========== ГЛАВНОЕ ОКНО (ВКЛАДКИ) ==========
- class App:
- def __init__(self):
- self.root = tk.Tk()
- self.root.title("Учёт оборудования (партионный)")
- self.root.geometry("1300x650")
- self.root.configure(bg="#f0f0f0")
- style = ttk.Style()
- style.theme_use("clam")
- style.configure("TNotebook.Tab", font=("Arial", 10, "bold"), padding=[10, 5])
- style.configure("TButton", font=("Arial", 9), padding=4)
- style.configure("Treeview.Heading", font=("Arial", 10, "bold"))
- self.notebook = ttk.Notebook(self.root)
- self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
- # Вкладка "Остатки оборудования"
- self.stock_frame = ttk.Frame(self.notebook)
- self.notebook.add(self.stock_frame, text="📦 Остатки оборудования")
- self.create_stock_tab()
- # Вкладка "Закупки"
- self.purchase_frame = ttk.Frame(self.notebook)
- self.notebook.add(self.purchase_frame, text="📥 Закупки")
- self.create_purchase_tab()
- # Вкладка "Списания"
- self.writeoff_frame = ttk.Frame(self.notebook)
- self.notebook.add(self.writeoff_frame, text="📤 Списания")
- self.create_writeoff_tab()
- self.root.mainloop()
- def create_stock_tab(self):
- # Поиск
- search_frame = ttk.Frame(self.stock_frame)
- search_frame.pack(fill=tk.X, padx=5, pady=5)
- ttk.Label(search_frame, text="Поиск:").pack(side=tk.LEFT, padx=5)
- self.stock_search = ttk.Entry(search_frame, width=30)
- self.stock_search.pack(side=tk.LEFT, padx=5)
- ttk.Button(search_frame, text="🔍 Найти", command=lambda: search_stock(self.stock_tree, self.stock_search)).pack(side=tk.LEFT, padx=2)
- ttk.Button(search_frame, text="🔄 Сброс", command=lambda: refresh_stock(self.stock_tree)).pack(side=tk.LEFT, padx=2)
- columns = ("ID", "Наименование", "Производитель", "Категория", "Остаток (шт)")
- self.stock_tree = ttk.Treeview(self.stock_frame, columns=columns, show="headings")
- for col in columns:
- self.stock_tree.heading(col, text=col)
- self.stock_tree.column(col, width=150)
- self.stock_tree.pack(fill=tk.BOTH, expand=True)
- btn_frame = ttk.Frame(self.stock_frame)
- btn_frame.pack(fill=tk.X, padx=5, pady=5)
- ttk.Button(btn_frame, text="🔄 Обновить", command=lambda: refresh_stock(self.stock_tree)).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="📎 Экспорт CSV", command=lambda: export_to_csv(self.stock_tree, "stock")).pack(side=tk.LEFT, padx=5)
- refresh_stock(self.stock_tree)
- def create_purchase_tab(self):
- # Панель поиска (можно по оборудованию или поставщику)
- search_frame = ttk.Frame(self.purchase_frame)
- search_frame.pack(fill=tk.X, padx=5, pady=5)
- ttk.Label(search_frame, text="Поиск (оборудование/поставщик):").pack(side=tk.LEFT, padx=5)
- self.purchase_search = ttk.Entry(search_frame, width=30)
- self.purchase_search.pack(side=tk.LEFT, padx=5)
- def search_purchases():
- kw = self.purchase_search.get().strip()
- for item in self.purchase_tree.get_children():
- self.purchase_tree.delete(item)
- if not kw:
- refresh_purchases(self.purchase_tree)
- return
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- SELECT id, equipment, supplier, warehouse, quantity, price_per_unit, total_cost, purchase_date, notes
- FROM PurchaseDetails
- WHERE equipment ILIKE %s OR supplier ILIKE %s
- ORDER BY purchase_date DESC
- """, (f'{kw}%', f'{kw}%')) # поиск с начала
- for row in cur.fetchall():
- self.purchase_tree.insert("", tk.END, values=row)
- ttk.Button(search_frame, text="🔍 Найти", command=search_purchases).pack(side=tk.LEFT, padx=2)
- ttk.Button(search_frame, text="🔄 Сброс", command=lambda: refresh_purchases(self.purchase_tree)).pack(side=tk.LEFT, padx=2)
- columns = ("ID", "Оборудование", "Поставщик", "Склад", "Кол-во", "Цена за ед.", "Итого", "Дата", "Примечания")
- self.purchase_tree = ttk.Treeview(self.purchase_frame, columns=columns, show="headings")
- for col in columns:
- self.purchase_tree.heading(col, text=col)
- self.purchase_tree.column(col, width=100)
- self.purchase_tree.pack(fill=tk.BOTH, expand=True)
- btn_frame = ttk.Frame(self.purchase_frame)
- btn_frame.pack(fill=tk.X, padx=5, pady=5)
- ttk.Button(btn_frame, text="➕ Добавить закупку", command=lambda: add_purchase(self.root, lambda: refresh_purchases(self.purchase_tree))).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="✏️ Редактировать", command=lambda: edit_purchase(self.purchase_tree, lambda: refresh_purchases(self.purchase_tree))).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="❌ Удалить", command=lambda: delete_purchase(self.purchase_tree, lambda: refresh_purchases(self.purchase_tree))).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="🔄 Обновить", command=lambda: refresh_purchases(self.purchase_tree)).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="📎 Экспорт CSV", command=lambda: export_to_csv(self.purchase_tree, "purchases")).pack(side=tk.LEFT, padx=5)
- refresh_purchases(self.purchase_tree)
- def create_writeoff_tab(self):
- # Поиск по оборудованию/сотруднику
- search_frame = ttk.Frame(self.writeoff_frame)
- search_frame.pack(fill=tk.X, padx=5, pady=5)
- ttk.Label(search_frame, text="Поиск (оборудование/сотрудник):").pack(side=tk.LEFT, padx=5)
- self.writeoff_search = ttk.Entry(search_frame, width=30)
- self.writeoff_search.pack(side=tk.LEFT, padx=5)
- def search_writeoffs():
- kw = self.writeoff_search.get().strip()
- for item in self.writeoff_tree.get_children():
- self.writeoff_tree.delete(item)
- if not kw:
- refresh_writeoffs(self.writeoff_tree)
- return
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- SELECT id, equipment, employee, quantity, reason, write_off_date
- FROM WriteOffDetails
- WHERE equipment ILIKE %s OR employee ILIKE %s
- ORDER BY write_off_date DESC
- """, (f'{kw}%', f'{kw}%'))
- for row in cur.fetchall():
- self.writeoff_tree.insert("", tk.END, values=row)
- ttk.Button(search_frame, text="🔍 Найти", command=search_writeoffs).pack(side=tk.LEFT, padx=2)
- ttk.Button(search_frame, text="🔄 Сброс", command=lambda: refresh_writeoffs(self.writeoff_tree)).pack(side=tk.LEFT, padx=2)
- columns = ("ID", "Оборудование", "Сотрудник", "Кол-во", "Причина", "Дата списания")
- self.writeoff_tree = ttk.Treeview(self.writeoff_frame, columns=columns, show="headings")
- for col in columns:
- self.writeoff_tree.heading(col, text=col)
- self.writeoff_tree.column(col, width=140)
- self.writeoff_tree.pack(fill=tk.BOTH, expand=True)
- btn_frame = ttk.Frame(self.writeoff_frame)
- btn_frame.pack(fill=tk.X, padx=5, pady=5)
- ttk.Button(btn_frame, text="➕ Добавить списание", command=lambda: add_writeoff(self.root, lambda: refresh_writeoffs(self.writeoff_tree))).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="❌ Удалить", command=lambda: delete_writeoff(self.writeoff_tree, lambda: refresh_writeoffs(self.writeoff_tree))).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="🔄 Обновить", command=lambda: refresh_writeoffs(self.writeoff_tree)).pack(side=tk.LEFT, padx=5)
- ttk.Button(btn_frame, text="📎 Экспорт CSV", command=lambda: export_to_csv(self.writeoff_tree, "writeoffs")).pack(side=tk.LEFT, padx=5)
- refresh_writeoffs(self.writeoff_tree)
- # Функция редактирования закупки (дополнение к классу, но вынесем отдельно)
- def edit_purchase(tree, refresh_callback):
- selected = tree.selection()
- if not selected:
- messagebox.showwarning("Выбор", "Выберите закупку")
- return
- item = tree.item(selected[0])
- purchase_id = item['values'][0]
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("SELECT equipment_id, supplier_id, warehouse_id, quantity, price_per_unit, purchase_date, notes FROM Purchase WHERE id = %s", (purchase_id,))
- data = cur.fetchone()
- if not data:
- return
- eq_id, sup_id, wh_id, qty, price, date, notes = data
- win = tk.Toplevel()
- win.title("Редактирование закупки")
- win.geometry("500x400")
- win.grab_set()
- # Получаем названия для предзаполнения
- eq_name = next((f"{e[0]} - {e[1]}" for e in get_equipment_list() if e[0] == eq_id), "")
- sup_name = next((f"{s[0]} - {s[1]}" for s in get_suppliers() if s[0] == sup_id), "")
- wh_name = next((f"{w[0]} - {w[1]}" for w in get_warehouses() if w[0] == wh_id), "")
- tk.Label(win, text="Оборудование:").grid(row=0, column=0, sticky="e")
- equip_var = tk.StringVar(value=eq_name)
- equip_combo = ttk.Combobox(win, textvariable=equip_var, values=[f"{e[0]} - {e[1]}" for e in get_equipment_list()], width=40)
- equip_combo.grid(row=0, column=1, padx=5, pady=5)
- tk.Label(win, text="Поставщик:").grid(row=1, column=0, sticky="e")
- supp_var = tk.StringVar(value=sup_name)
- supp_combo = ttk.Combobox(win, textvariable=supp_var, values=[f"{s[0]} - {s[1]}" for s in get_suppliers()], width=40)
- supp_combo.grid(row=1, column=1, padx=5, pady=5)
- tk.Label(win, text="Склад:").grid(row=2, column=0, sticky="e")
- wh_var = tk.StringVar(value=wh_name)
- wh_combo = ttk.Combobox(win, textvariable=wh_var, values=[f"{w[0]} - {w[1]}" for w in get_warehouses()], width=40)
- wh_combo.grid(row=2, column=1, padx=5, pady=5)
- tk.Label(win, text="Количество:").grid(row=3, column=0, sticky="e")
- qty_entry = tk.Entry(win)
- qty_entry.insert(0, str(qty))
- qty_entry.grid(row=3, column=1, padx=5, pady=5)
- tk.Label(win, text="Цена за ед.:").grid(row=4, column=0, sticky="e")
- price_entry = tk.Entry(win)
- price_entry.insert(0, str(price))
- price_entry.grid(row=4, column=1, padx=5, pady=5)
- tk.Label(win, text="Дата (ГГГГ-ММ-ДД):").grid(row=5, column=0, sticky="e")
- date_entry = tk.Entry(win)
- date_entry.insert(0, date.strftime("%Y-%m-%d") if isinstance(date, datetime) else date)
- date_entry.grid(row=5, column=1, padx=5, pady=5)
- tk.Label(win, text="Примечание:").grid(row=6, column=0, sticky="ne")
- notes_text = tk.Text(win, height=4, width=30)
- notes_text.insert("1.0", notes or "")
- notes_text.grid(row=6, column=1, padx=5, pady=5)
- def save_edit():
- try:
- eq_id_new = int(equip_var.get().split(" - ")[0])
- sup_id_new = int(supp_var.get().split(" - ")[0])
- wh_id_new = int(wh_var.get().split(" - ")[0])
- new_qty = int(qty_entry.get())
- new_price = float(price_entry.get())
- new_date = date_entry.get()
- datetime.strptime(new_date, "%Y-%m-%d")
- new_notes = notes_text.get("1.0", tk.END).strip()
- except Exception:
- messagebox.showerror("Ошибка", "Проверьте данные")
- return
- with get_conn() as conn, conn.cursor() as cur:
- cur.execute("""
- UPDATE Purchase
- SET equipment_id=%s, supplier_id=%s, warehouse_id=%s, quantity=%s,
- price_per_unit=%s, purchase_date=%s, notes=%s
- WHERE id=%s
- """, (eq_id_new, sup_id_new, wh_id_new, new_qty, new_price, new_date, new_notes, purchase_id))
- conn.commit()
- win.destroy()
- refresh_callback()
- messagebox.showinfo("Успех", "Закупка обновлена")
- tk.Button(win, text="Сохранить", command=save_edit, bg="lightblue").grid(row=7, column=0, columnspan=2, pady=15)
- if __name__ == "__main__":
- App()
|