Загрузка данных
import customtkinter as ctk
import mysql.connector
from tkinter import messagebox, ttk
import re
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
class AutoServiceApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title("Автосервис")
self.geometry("450x550")
self.db_config = {'host': 'LocalHost', 'user': 'root', 'password': 'Ruslan2020gog', 'database': 'autoservice_db'}
self.current_user = None
self.services_data = {}
self.mechanics_data = {}
self.show_login_screen()
def connect(self):
return mysql.connector.connect(**self.db_config)
def show_login_screen(self):
for widget in self.winfo_children(): widget.destroy()
self.geometry("450x550")
ctk.CTkLabel(self, text="Авторизация", font=("Arial", 26, "bold")).pack(pady=(50, 30))
self.login_entry = ctk.CTkEntry(self, placeholder_text="Логин", width=300, height=40)
self.login_entry.pack(pady=10)
self.pass_entry = ctk.CTkEntry(self, placeholder_text="Пароль", show="*", width=300, height=40)
self.pass_entry.pack(pady=10)
ctk.CTkButton(self, text="Войти", width=300, height=45, font=("Arial", 16, "bold"), command=self.login).pack(pady=20)
ctk.CTkButton(self, text="Регистрация", width=300, height=45, fg_color="transparent", border_width=2, border_color="#3b8ed0", command=self.show_register_screen).pack()
def show_register_screen(self):
for widget in self.winfo_children(): widget.destroy()
ctk.CTkLabel(self, text="Регистрация", font=("Arial", 26, "bold")).pack(pady=(40, 20))
self.reg_login = ctk.CTkEntry(self, placeholder_text="Логин", width=300, height=40)
self.reg_login.pack(pady=10)
self.reg_mail = ctk.CTkEntry(self, placeholder_text="Почта", width=300, height=40)
self.reg_mail.pack(pady=10)
self.reg_pass = ctk.CTkEntry(self, placeholder_text="Пароль", show="*", width=300, height=40)
self.reg_pass.pack(pady=10)
ctk.CTkButton(self, text="Создать аккаунт", width=300, height=45, command=self.register).pack(pady=20)
ctk.CTkButton(self, text="Назад", fg_color="transparent", command=self.show_login_screen).pack()
def validate_password(self, p):
if len(p) < 4: return False, "Пароль слишком короткий!"
if not re.search(r"[A-Z]", p): return False, "Нужна заглавная буква!"
if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", p): return False, "Нужен спецсимвол!"
if re.search(r"[а-яА-Я]", p): return False, "Только латиница!"
return True, ""
def validate_mail(self, m):
if len(m) < 14: return False, "Длина почты слишком короткая!"
if not re.search(r"[@]", m): return False, "не найдено символа @"
if not re.search(r"[.com]", m): return False, "почта введена неверно"
if not re.search(r"[.net]", m): return False, "почта введена неверно"
if not re.search(r"[.ru]", m): return False, "почта введена неверно"
if re.search(r"[а-яА-Я]", m): return False, "Только латиница!"
return True, ""
def register(self):
u, p, m = self.reg_login.get().strip(), self.reg_pass.get().strip()
if not u or not p or not m: return
v, msg = self.validate_password(p)
m, msg = self.validate_mail(m)
if not v:
messagebox.showerror("Ошибка", msg)
return
if not m:
messagebox.showerror("Ошибка", msg)
return
try:
conn = self.connect(); cursor = conn.cursor()
cursor.execute("INSERT INTO users (username, password, role, mail) VALUES (%s, %s, 'client', %s)", (u, p))
conn.commit(); conn.close(); self.show_login_screen()
except: messagebox.showerror("Ошибка", "Логин занят")
def login(self):
u, p = self.login_entry.get().strip(), self.pass_entry.get().strip()
try:
conn = self.connect(); cursor = conn.cursor()
cursor.execute("SELECT username, role FROM users WHERE username=%s AND password=%s", (u, p))
user = cursor.fetchone(); conn.close()
if user:
self.current_user = user
self.load_dictionaries(); self.show_main_interface()
else: messagebox.showerror("Ошибка", "Неверные данные")
except Exception as e: messagebox.showerror("БД", str(e))
def load_dictionaries(self):
conn = self.connect(); cursor = conn.cursor()
cursor.execute("SELECT id, service_name FROM services")
self.services_data = {name: id for id, name in cursor.fetchall()}
cursor.execute("SELECT id, fio FROM mechanics")
self.mechanics_data = {fio: id for id, fio in cursor.fetchall()}
conn.close()
def show_main_interface(self):
for widget in self.winfo_children(): widget.destroy()
self.geometry("1300x750")
tp = ctk.CTkFrame(self, height=60, fg_color="#212121")
tp.pack(fill="x", padx=10, pady=5)
ctk.CTkLabel(tp, text=f"Пользователь: {self.current_user[0]}", font=("Arial", 15, "bold")).pack(side="left", padx=20)
ctk.CTkButton(tp, text="Выйти", width=100, fg_color="#e74c3c", command=self.show_login_screen).pack(side="right", padx=20)
cont = ctk.CTkFrame(self)
cont.pack(expand=True, fill="both", padx=10, pady=10)
cont.grid_columnconfigure(1, weight=1); cont.grid_rowconfigure(0, weight=1)
sb = ctk.CTkFrame(cont, width=350); sb.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
ctk.CTkLabel(sb, text="Оформление записи", font=("Arial", 20, "bold")).pack(pady=15)
self.client_name = ctk.CTkEntry(sb, placeholder_text="Ваше ФИО", width=280); self.client_name.pack(pady=5)
self.car_model = ctk.CTkEntry(sb, placeholder_text="Автомобиль", width=280); self.car_model.pack(pady=5)
ctk.CTkLabel(sb, text="Услуга:").pack()
self.service_cb = ctk.CTkComboBox(sb, values=list(self.services_data.keys()), width=280); self.service_cb.pack(pady=5)
ctk.CTkLabel(sb, text="Мастер:").pack()
self.mechanic_cb = ctk.CTkComboBox(sb, values=list(self.mechanics_data.keys()), width=280); self.mechanic_cb.pack(pady=5)
self.app_date = ctk.CTkEntry(sb, placeholder_text="Дата (ГГГГ-ММ-ДД)", width=280); self.app_date.pack(pady=10)
ctk.CTkButton(sb, text="Создать запись", fg_color="#2ecc71", command=self.confirm_creation).pack(pady=10)
ctk.CTkButton(sb, text="Удалить запись", fg_color="#e74c3c", command=self.confirm_deletion).pack(pady=5)
tf = ctk.CTkFrame(cont); tf.grid(row=0, column=1, sticky="nsew", padx=10, pady=10)
self.tree = ttk.Treeview(tf, columns=("ID", "Клиент", "Авто", "Услуга", "Цена", "Мастер", "Дата", "Номер", "VIN-Номер"), show='headings')
for col, w in [("ID", 40), ("Клиент", 180), ("Авто", 130), ("Услуга", 160), ("Цена", 70), ("Мастер", 180), ("Дата", 100), ("Номер", 80), ("VIN-Номер", 80)]:
self.tree.heading(col, text=col); self.tree.column(col, width=w, anchor="center")
self.tree.pack(expand=True, fill="both", padx=10, pady=10); self.refresh_data()
def confirm_creation(self):
if messagebox.askyesno("Запись", "Подтвердить?"): self.save_record()
def save_record(self):
try:
s_id, m_id = self.services_data[self.service_cb.get()], self.mechanics_data[self.mechanic_cb.get()]
conn = self.connect(); cursor = conn.cursor()
cursor.execute("INSERT INTO clients (full_name, phone) VALUES (%s, %s)", (self.client_name.get(), self.current_user[0]))
c_id = cursor.lastrowid
cursor.execute("INSERT INTO cars (client_id, brand_model) VALUES (%s, %s)", (c_id, self.car_model.get()))
car_id = cursor.lastrowid
cursor.execute("INSERT INTO appointments (car_id, service_id, mechanic_id, appointment_date) VALUES (%s, %s, %s, %s)", (car_id, s_id, m_id, self.app_date.get()))
conn.commit(); conn.close(); self.refresh_data()
except Exception as e: messagebox.showerror("Ошибка", str(e))
def confirm_deletion(self):
sel = self.tree.selection()
if not sel: return
v = self.tree.item(sel[0])['values']
rec_id = v[0]
conn = self.connect(); cursor = conn.cursor()
cursor.execute("SELECT c.phone FROM clients c JOIN cars cr ON c.id = cr.client_id JOIN appointments a ON cr.id = a.car_id WHERE a.id = %s", (rec_id,))
res = cursor.fetchone(); conn.close()
if self.current_user[1] == 'admin' or (res and str(res[0]) == str(self.current_user[0])):
if messagebox.askyesno("Удаление", "Удалить?"):
conn = self.connect(); cursor = conn.cursor()
cursor.execute("DELETE FROM appointments WHERE id = %s", (rec_id,)); conn.commit(); conn.close(); self.refresh_data()
else: messagebox.showerror("Отказ", "Это не ваша запись")
def refresh_data(self):
for i in self.tree.get_children(): self.tree.delete(i)
conn = self.connect(); cursor = conn.cursor()
cursor.execute("SELECT a.id, c.full_name, cr.brand_model, s.service_name, s.price, m.fio, a.appointment_date FROM appointments a LEFT JOIN cars cr ON a.car_id = cr.id LEFT JOIN clients c ON cr.client_id = c.id LEFT JOIN services s ON a.service_id = s.id LEFT JOIN mechanics m ON a.mechanic_id = m.id ORDER BY a.id DESC")
for r in cursor.fetchall(): self.tree.insert("", "end", values=r)
conn.close()
if __name__ == "__main__":
app = AutoServiceApp(); app.mainloop()