Compare commits
42
Commits
785c873eda
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f349274710 | ||
|
|
4c8221971a | ||
|
|
5bd728b1bd | ||
|
|
9053434a83 | ||
|
|
e8edfe939f | ||
|
|
17bdf516b3 | ||
|
|
94e45a7aad | ||
|
|
54c14826c4 | ||
|
|
52233b09be | ||
|
|
3ab63e6327 | ||
|
|
e8bc7113d1 | ||
|
|
509715f629 | ||
|
|
c4b6e1fba9 | ||
|
|
e5c6da3417 | ||
|
|
92abbb95fa | ||
|
|
6e6218f2fa | ||
|
|
3dc145beae | ||
|
|
78c730f245 | ||
|
|
984b1ecba5 | ||
|
|
eaf12c10a4 | ||
|
|
29f0700324 | ||
|
|
2a9b7a4edc | ||
|
|
787bf561e4 | ||
|
|
b1a5224a67 | ||
|
|
a710f36393 | ||
|
|
67c6169a7e | ||
|
|
20386a867a | ||
|
|
f62c4904cd | ||
|
|
6359376da0 | ||
|
|
5f55320865 | ||
|
|
02aa1da64f | ||
|
|
db80a043d7 | ||
|
|
69e133c6e3 | ||
|
|
40a0ed8d5b | ||
|
|
deafb9c440 | ||
|
|
2482a8d090 | ||
|
|
9ae17ff2b7 | ||
|
|
10b5cc4a02 | ||
|
|
130a6e0a4f | ||
|
|
91cfa7c8c8 | ||
|
|
85e501b109 | ||
|
|
a3ae8295b9 |
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+197
-99
@@ -1,126 +1,224 @@
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox, filedialog
|
||||
from tkinter import messagebox
|
||||
from database import connect
|
||||
import os
|
||||
import shutil
|
||||
|
||||
class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
self.selected_image_path = None
|
||||
class AdminMenu:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.frame = tk.Frame(root)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
# --- Header ---
|
||||
top = tk.Frame(self, bg="#333")
|
||||
top.pack(fill="x")
|
||||
tk.Label(top, text="ADMIN DASHBOARD", font=("Arial", 16, "bold"), fg="white", bg="#333").pack(side="left", padx=10, pady=10)
|
||||
tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: self.controller.show_frame("LoginPage")).pack(side="right", padx=10)
|
||||
# =====================
|
||||
# JUDUL
|
||||
# =====================
|
||||
tk.Label(
|
||||
self.frame,
|
||||
text="ADMIN - KELOLA MENU",
|
||||
font=("Arial", 18, "bold")
|
||||
).pack(pady=15)
|
||||
|
||||
# --- Form Input (Kiri) ---
|
||||
left_frame = tk.Frame(self, padx=10, pady=10)
|
||||
left_frame.pack(side="left", fill="y")
|
||||
# =====================
|
||||
# FORM INPUT
|
||||
# =====================
|
||||
form = tk.Frame(self.frame)
|
||||
form.pack(pady=10)
|
||||
|
||||
tk.Label(left_frame, text="Nama Menu:").pack(anchor="w")
|
||||
self.entry_nama = tk.Entry(left_frame, width=30)
|
||||
self.entry_nama.pack(pady=5)
|
||||
tk.Label(form, text="Nama Menu", width=15, anchor="w")\
|
||||
.grid(row=0, column=0, pady=5)
|
||||
self.nama = tk.Entry(form, width=30)
|
||||
self.nama.grid(row=0, column=1)
|
||||
|
||||
tk.Label(left_frame, text="Harga:").pack(anchor="w")
|
||||
self.entry_harga = tk.Entry(left_frame, width=30)
|
||||
self.entry_harga.pack(pady=5)
|
||||
tk.Label(form, text="Kategori", width=15, anchor="w")\
|
||||
.grid(row=1, column=0, pady=5)
|
||||
self.kategori = tk.StringVar()
|
||||
tk.OptionMenu(
|
||||
form,
|
||||
self.kategori,
|
||||
"Makanan",
|
||||
"Minuman",
|
||||
"Dessert"
|
||||
).grid(row=1, column=1, sticky="w")
|
||||
self.kategori.set("Makanan")
|
||||
|
||||
tk.Label(left_frame, text="Stok Awal:").pack(anchor="w")
|
||||
self.entry_stok = tk.Entry(left_frame, width=30)
|
||||
self.entry_stok.pack(pady=5)
|
||||
tk.Label(form, text="Harga", width=15, anchor="w")\
|
||||
.grid(row=2, column=0, pady=5)
|
||||
self.harga = tk.Entry(form, width=30)
|
||||
self.harga.grid(row=2, column=1)
|
||||
|
||||
tk.Label(left_frame, text="Gambar:").pack(anchor="w")
|
||||
self.btn_img = tk.Button(left_frame, text="Pilih Gambar", command=self.browse_image)
|
||||
self.btn_img.pack(pady=5, anchor="w")
|
||||
self.lbl_img_path = tk.Label(left_frame, text="Belum ada gambar", fg="gray", font=("Arial", 8))
|
||||
self.lbl_img_path.pack(anchor="w")
|
||||
tk.Label(form, text="Path Gambar", width=15, anchor="w")\
|
||||
.grid(row=3, column=0, pady=5)
|
||||
self.gambar = tk.Entry(form, width=30)
|
||||
self.gambar.grid(row=3, column=1)
|
||||
|
||||
# Tombol Aksi
|
||||
btn_box = tk.Frame(left_frame, pady=20)
|
||||
btn_box.pack()
|
||||
tk.Button(btn_box, text="TAMBAH", bg="#4CAF50", fg="white", command=self.add_menu).grid(row=0, column=0, padx=5)
|
||||
tk.Button(btn_box, text="HAPUS", bg="#f44336", fg="white", command=self.delete_menu).grid(row=0, column=1, padx=5)
|
||||
# =====================
|
||||
# BUTTON CRUD
|
||||
# =====================
|
||||
btn_frame = tk.Frame(self.frame)
|
||||
btn_frame.pack(pady=10)
|
||||
|
||||
# --- Tabel Menu (Kanan) ---
|
||||
right_frame = tk.Frame(self, padx=10, pady=10)
|
||||
right_frame.pack(side="right", fill="both", expand=True)
|
||||
tk.Button(btn_frame, text="Tambah", width=10,
|
||||
bg="#d1e7dd", command=self.tambah).grid(row=0, column=0, padx=5)
|
||||
|
||||
tk.Label(right_frame, text="Daftar Menu Database:", font=("Arial", 10, "bold")).pack(anchor="w")
|
||||
tk.Button(btn_frame, text="Update", width=10,
|
||||
bg="#cfe2ff", command=self.update).grid(row=0, column=1, padx=5)
|
||||
|
||||
self.list_menu = tk.Listbox(right_frame)
|
||||
self.list_menu.pack(fill="both", expand=True)
|
||||
tk.Button(btn_frame, text="Hapus", width=10,
|
||||
bg="#f8d7da", command=self.hapus).grid(row=0, column=2, padx=5)
|
||||
|
||||
scrollbar = tk.Scrollbar(self.list_menu)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
self.list_menu.config(yscrollcommand=scrollbar.set)
|
||||
scrollbar.config(command=self.list_menu.yview)
|
||||
tk.Button(btn_frame, text="Refresh", width=10,
|
||||
command=self.load_menu).grid(row=0, column=3, padx=5)
|
||||
|
||||
def update_data(self):
|
||||
self.list_menu.delete(0, tk.END)
|
||||
self.data_menu = []
|
||||
tk.Button(btn_frame, text="Logout", width=10,
|
||||
command=self.logout).grid(row=0, column=4, padx=5)
|
||||
|
||||
# =====================
|
||||
# FILTER
|
||||
# =====================
|
||||
filter_frame = tk.Frame(self.frame)
|
||||
filter_frame.pack(pady=5)
|
||||
|
||||
tk.Label(filter_frame, text="Filter Kategori:").pack(side=tk.LEFT)
|
||||
self.filter_kategori = tk.StringVar()
|
||||
tk.OptionMenu(
|
||||
filter_frame,
|
||||
self.filter_kategori,
|
||||
"Semua",
|
||||
"Makanan",
|
||||
"Minuman",
|
||||
"Dessert",
|
||||
command=self.load_menu
|
||||
).pack(side=tk.LEFT)
|
||||
self.filter_kategori.set("Semua")
|
||||
|
||||
# =====================
|
||||
# LIST MENU
|
||||
# =====================
|
||||
self.listbox = tk.Listbox(self.frame, width=80, height=10)
|
||||
self.listbox.pack(pady=10)
|
||||
self.listbox.bind("<<ListboxSelect>>", self.pilih_menu)
|
||||
|
||||
self.menu_data = []
|
||||
self.load_menu()
|
||||
|
||||
# =====================
|
||||
# LOAD MENU + FILTER
|
||||
# =====================
|
||||
def load_menu(self, *_):
|
||||
self.listbox.delete(0, tk.END)
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("SELECT id, nama, harga, stok FROM menu")
|
||||
rows = cur.fetchall()
|
||||
|
||||
if self.filter_kategori.get() == "Semua":
|
||||
cur.execute("SELECT * FROM menu")
|
||||
else:
|
||||
cur.execute(
|
||||
"SELECT * FROM menu WHERE kategori=?",
|
||||
(self.filter_kategori.get(),)
|
||||
)
|
||||
|
||||
self.menu_data = cur.fetchall()
|
||||
db.close()
|
||||
|
||||
for row in rows:
|
||||
self.data_menu.append(row)
|
||||
self.list_menu.insert(tk.END, f"{row[1]} - Rp {int(row[2])} (Stok: {row[3]})")
|
||||
for m in self.menu_data:
|
||||
self.listbox.insert(
|
||||
tk.END,
|
||||
f"{m[0]:<3} | {m[1]:<20} | {m[2]:<10} | Rp {m[3]:,.0f}"
|
||||
)
|
||||
|
||||
def browse_image(self):
|
||||
filename = filedialog.askopenfilename(title="Pilih Gambar", filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")])
|
||||
if filename:
|
||||
self.selected_image_path = filename
|
||||
self.lbl_img_path.config(text=os.path.basename(filename), fg="black")
|
||||
|
||||
def add_menu(self):
|
||||
nama = self.entry_nama.get()
|
||||
harga = self.entry_harga.get()
|
||||
stok = self.entry_stok.get()
|
||||
|
||||
if not nama or not harga:
|
||||
messagebox.showwarning("Warning", "Nama dan Harga wajib diisi!")
|
||||
return
|
||||
|
||||
final_image_path = "default.png"
|
||||
if self.selected_image_path:
|
||||
if not os.path.exists("img"): os.makedirs("img")
|
||||
nama_file_asli = os.path.basename(self.selected_image_path)
|
||||
destinasi = os.path.join("img", nama_file_asli)
|
||||
shutil.copy(self.selected_image_path, destinasi)
|
||||
final_image_path = destinasi
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
try:
|
||||
cur.execute("INSERT INTO menu (nama, harga, stok, gambar) VALUES (?, ?, ?, ?)",
|
||||
(nama, harga, stok if stok else 0, final_image_path))
|
||||
db.commit()
|
||||
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan!")
|
||||
self.entry_nama.delete(0, tk.END)
|
||||
self.entry_harga.delete(0, tk.END)
|
||||
self.entry_stok.delete(0, tk.END)
|
||||
self.update_data()
|
||||
except Exception as e:
|
||||
messagebox.showerror("Error", str(e))
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def delete_menu(self):
|
||||
idx = self.list_menu.curselection()
|
||||
# =====================
|
||||
# PILIH MENU
|
||||
# =====================
|
||||
def pilih_menu(self, event):
|
||||
idx = self.listbox.curselection()
|
||||
if not idx:
|
||||
messagebox.showwarning("Pilih", "Pilih menu dulu!")
|
||||
return
|
||||
|
||||
id_menu = self.data_menu[idx[0]][0]
|
||||
if messagebox.askyesno("Hapus", "Yakin hapus menu ini?"):
|
||||
menu = self.menu_data[idx[0]]
|
||||
self.clear_form()
|
||||
|
||||
self.nama.insert(0, menu[1])
|
||||
self.kategori.set(menu[2])
|
||||
self.harga.insert(0, menu[3])
|
||||
self.gambar.insert(0, menu[4])
|
||||
|
||||
# =====================
|
||||
# CRUD
|
||||
# =====================
|
||||
def tambah(self):
|
||||
if not self.nama.get() or not self.harga.get():
|
||||
messagebox.showwarning("Error", "Data belum lengkap")
|
||||
return
|
||||
|
||||
db = connect()
|
||||
db.cursor().execute("DELETE FROM menu WHERE id=?", (id_menu,))
|
||||
cur = db.cursor()
|
||||
cur.execute(
|
||||
"INSERT INTO menu(nama,kategori,harga,gambar) VALUES (?,?,?,?)",
|
||||
(
|
||||
self.nama.get(),
|
||||
self.kategori.get(),
|
||||
float(self.harga.get()),
|
||||
self.gambar.get()
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
db.close()
|
||||
self.update_data()
|
||||
|
||||
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan")
|
||||
self.load_menu()
|
||||
self.clear_form()
|
||||
|
||||
def update(self):
|
||||
idx = self.listbox.curselection()
|
||||
if not idx:
|
||||
messagebox.showwarning("Pilih", "Pilih menu dulu")
|
||||
return
|
||||
|
||||
menu_id = self.menu_data[idx[0]][0]
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute(
|
||||
"UPDATE menu SET nama=?, kategori=?, harga=?, gambar=? WHERE id=?",
|
||||
(
|
||||
self.nama.get(),
|
||||
self.kategori.get(),
|
||||
float(self.harga.get()),
|
||||
self.gambar.get(),
|
||||
menu_id
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
messagebox.showinfo("Sukses", "Menu berhasil diupdate")
|
||||
self.load_menu()
|
||||
|
||||
def hapus(self):
|
||||
idx = self.listbox.curselection()
|
||||
if not idx:
|
||||
messagebox.showwarning("Pilih", "Pilih menu dulu")
|
||||
return
|
||||
|
||||
menu_id = self.menu_data[idx[0]][0]
|
||||
|
||||
if messagebox.askyesno("Konfirmasi", "Yakin hapus menu ini?"):
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("DELETE FROM menu WHERE id=?", (menu_id,))
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
messagebox.showinfo("Sukses", "Menu berhasil dihapus")
|
||||
self.load_menu()
|
||||
self.clear_form()
|
||||
|
||||
def clear_form(self):
|
||||
self.nama.delete(0, tk.END)
|
||||
self.harga.delete(0, tk.END)
|
||||
self.gambar.delete(0, tk.END)
|
||||
self.kategori.set("Makanan")
|
||||
|
||||
def logout(self):
|
||||
self.frame.destroy()
|
||||
from main import LoginScreen
|
||||
LoginScreen(self.root)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 308 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
+51
-20
@@ -7,7 +7,9 @@ def setup_database():
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
|
||||
# 1. Users
|
||||
# =====================
|
||||
# TABEL USERS
|
||||
# =====================
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS users(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -17,58 +19,87 @@ def setup_database():
|
||||
)
|
||||
""")
|
||||
|
||||
# 2. Menu
|
||||
# =====================
|
||||
# TABEL MENU (ADA KATEGORI)
|
||||
# =====================
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS menu(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nama TEXT,
|
||||
kategori TEXT,
|
||||
harga REAL,
|
||||
stok INTEGER,
|
||||
gambar TEXT
|
||||
)
|
||||
""")
|
||||
|
||||
# 3. Transaksi (Header)
|
||||
# Jaga-jaga DB lama (tidak error)
|
||||
try:
|
||||
cur.execute("ALTER TABLE menu ADD COLUMN kategori TEXT")
|
||||
except sqlite3.OperationalError:
|
||||
pass
|
||||
|
||||
# =====================
|
||||
# TABEL TRANSAKSI
|
||||
# =====================
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS transaksi(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
nama_pelanggan TEXT,
|
||||
meja_id INTEGER,
|
||||
tanggal TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
tanggal TEXT,
|
||||
total REAL,
|
||||
metode_pembayaran TEXT,
|
||||
status TEXT DEFAULT 'Pending'
|
||||
meja_id INTEGER,
|
||||
status TEXT,
|
||||
metode TEXT
|
||||
)
|
||||
""")
|
||||
# Status Flow: 'Pending' (Dipesan) -> 'Served' (Diantar Waiter) -> 'Paid' (Dibayar di Kasir)
|
||||
|
||||
# 4. Detail Transaksi
|
||||
# =====================
|
||||
# DETAIL TRANSAKSI
|
||||
# =====================
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS detail_transaksi(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
transaksi_id INTEGER,
|
||||
menu_id INTEGER,
|
||||
menu_nama TEXT,
|
||||
harga REAL,
|
||||
jumlah INTEGER,
|
||||
subtotal REAL
|
||||
)
|
||||
""")
|
||||
|
||||
# Seeding Data User Dummy
|
||||
# =====================
|
||||
# INSERT USER DEFAULT
|
||||
# =====================
|
||||
cur.execute("SELECT COUNT(*) FROM users")
|
||||
if cur.fetchone()[0] == 0:
|
||||
users = [
|
||||
("admin", "admin", "admin"),
|
||||
("kasir", "kasir", "kasir"),
|
||||
("pembeli", "pembeli", "pembeli"),
|
||||
("pemilik","pemilik","pemilik"),
|
||||
("waiter","waiter","waiter")
|
||||
("waiter", "waiter", "waiter"),
|
||||
("pemilik", "pemilik", "pemilik")
|
||||
]
|
||||
cur.executemany("INSERT INTO users(username,password,role) VALUES (?,?,?)", users)
|
||||
cur.executemany(
|
||||
"INSERT INTO users(username,password,role) VALUES (?,?,?)",
|
||||
users
|
||||
)
|
||||
|
||||
# =====================
|
||||
# INSERT MENU DEFAULT
|
||||
# =====================
|
||||
cur.execute("SELECT COUNT(*) FROM menu")
|
||||
if cur.fetchone()[0] == 0:
|
||||
menus = [
|
||||
('Ayam Goreng', 'Makanan', 20000, 'aset/ayam_goreng.jpg'),
|
||||
('Bakso', 'Makanan', 15000, 'aset/bakso.jpg'),
|
||||
('Mie Ayam', 'Makanan', 12000, 'aset/mie_ayam.jpg'),
|
||||
('Es Teh', 'Minuman', 5000, 'aset/es_teh.jpg'),
|
||||
('Es Teler', 'Dessert', 15000, 'aset/es_teler.jpg'),
|
||||
('Jus Jeruk', 'Minuman', 8000, 'aset/jus_jeruk.jpg')
|
||||
]
|
||||
cur.executemany(
|
||||
"INSERT INTO menu (nama, kategori, harga, gambar) VALUES (?,?,?,?)",
|
||||
menus
|
||||
)
|
||||
|
||||
db.commit()
|
||||
db.close()
|
||||
print("Database Ready bos!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_database()
|
||||
@@ -1,49 +0,0 @@
|
||||
import sqlite3
|
||||
import os
|
||||
# PERHATIKAN: Ini harus setup_database, BUKAN init_db
|
||||
from database import setup_database
|
||||
|
||||
def isi_data_awal():
|
||||
# 1. Pastikan database dan tabel sudah dibuat
|
||||
setup_database()
|
||||
|
||||
db_name = "cafe.db"
|
||||
|
||||
# 2. Cek folder aset
|
||||
# Pastikan kamu punya folder bernama 'aset' dan di dalamnya ada gambar-gambarnya
|
||||
if not os.path.exists("aset"):
|
||||
print("❌ WADUH! Folder 'aset' gak ketemu. Pastikan nama folder di kiri adalah 'aset'.")
|
||||
return
|
||||
|
||||
conn = sqlite3.connect(db_name)
|
||||
cur = conn.cursor()
|
||||
|
||||
print("♻️ Membersihkan data lama...")
|
||||
cur.execute("DELETE FROM menu")
|
||||
cur.execute("DELETE FROM sqlite_sequence WHERE name='menu'") # Reset ID jadi 1
|
||||
|
||||
# 3. Data Menu (Path gambar pakai 'aset/')
|
||||
menus = [
|
||||
("Ayam Goreng", "Makanan", 15000, 20, "aset/ayam_goreng.jpg"),
|
||||
("Bakso Urat", "Makanan", 12000, 15, "aset/bakso.jpg"),
|
||||
("Mie Ayam", "Makanan", 10000, 25, "aset/mie_ayam.jpg"),
|
||||
("Es Teh Manis", "Minuman", 3000, 50, "aset/es_teh.jpg"),
|
||||
("Jus Jeruk", "Minuman", 5000, 30, "aset/jus_jeruk.jpg"),
|
||||
]
|
||||
|
||||
print("📥 Sedang mengisi data menu...")
|
||||
|
||||
try:
|
||||
cur.executemany("""
|
||||
INSERT INTO menu (nama, kategori, harga, stok, gambar)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", menus)
|
||||
conn.commit()
|
||||
print("✅ MANTAP! Data menu berhasil masuk. Sekarang jalankan main.py!")
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
isi_data_awal()
|
||||
+321
-34
@@ -1,62 +1,349 @@
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from database import connect
|
||||
from datetime import datetime
|
||||
import qrcode
|
||||
from PIL import ImageTk
|
||||
|
||||
class KasirPage(tk.Frame):
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
class KasirPage:
|
||||
def __init__(self, parent, controller=None):
|
||||
self.parent = parent
|
||||
self.frame = tk.Frame(parent)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
# Header
|
||||
top = tk.Frame(self, bg="#ddd")
|
||||
top.pack(fill="x")
|
||||
tk.Button(top, text="Logout", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10, pady=5)
|
||||
tk.Label(top, text="KASIR - PEMBAYARAN", font=("Arial", 16, "bold"), bg="#ddd").pack(side="left", padx=10)
|
||||
# =====================
|
||||
# JUDUL
|
||||
# =====================
|
||||
tk.Label(
|
||||
self.frame,
|
||||
text="KASIR - PEMBAYARAN",
|
||||
font=("Arial", 18, "bold")
|
||||
).pack(pady=10)
|
||||
|
||||
# List
|
||||
tk.Label(self, text="Tagihan Belum Bayar (Status: Served):", font=("Arial", 11)).pack(pady=10)
|
||||
self.order_list = tk.Listbox(self, width=80, height=15)
|
||||
self.order_list.pack(pady=5)
|
||||
tk.Button(
|
||||
self.frame,
|
||||
text="Logout",
|
||||
bg="#f9e79f",
|
||||
command=self.logout
|
||||
).pack(pady=5)
|
||||
|
||||
tk.Button(self, text="Refresh Data", command=self.update_data).pack(pady=5)
|
||||
tk.Button(self, text="💰 PROSES BAYAR & LUNAS", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=20, pady=20)
|
||||
tk.Button(
|
||||
self.frame,
|
||||
text="Refresh",
|
||||
command=self.load_tagihan
|
||||
).pack(pady=3)
|
||||
|
||||
def update_data(self):
|
||||
self.order_list.delete(0, tk.END)
|
||||
self.data_orders = []
|
||||
# =====================
|
||||
# LIST TAGIHAN
|
||||
# =====================
|
||||
tk.Label(
|
||||
self.frame,
|
||||
text="Daftar Tagihan (Status: Disajikan)",
|
||||
font=("Arial", 12)
|
||||
).pack(pady=5)
|
||||
|
||||
self.listbox = tk.Listbox(self.frame, width=65, height=10)
|
||||
self.listbox.pack(pady=5)
|
||||
|
||||
# =====================
|
||||
# METODE PEMBAYARAN
|
||||
# =====================
|
||||
metode_frame = tk.Frame(self.frame)
|
||||
metode_frame.pack(pady=5)
|
||||
|
||||
tk.Label(metode_frame, text="Metode Pembayaran:").pack(side=tk.LEFT)
|
||||
|
||||
self.metode = tk.StringVar(value="Cash")
|
||||
tk.OptionMenu(
|
||||
metode_frame,
|
||||
self.metode,
|
||||
"Cash", "QRIS", "E-Wallet"
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# =====================
|
||||
# INPUT CASH
|
||||
# =====================
|
||||
cash_frame = tk.Frame(self.frame)
|
||||
cash_frame.pack(pady=5)
|
||||
|
||||
tk.Label(cash_frame, text="Uang Diterima (Cash):").pack(side=tk.LEFT)
|
||||
|
||||
self.uang_entry = tk.Entry(cash_frame, width=15)
|
||||
self.uang_entry.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# =====================
|
||||
# HITUNG KEMBALIAN
|
||||
# =====================
|
||||
def hitung_kembalian(event=None):
|
||||
try:
|
||||
# 1. Ambil transaksi yang sedang dipilih (Total tagihan)
|
||||
idx = self.listbox.curselection()
|
||||
if idx:
|
||||
teks_item = self.listbox.get(idx).split(" ")
|
||||
total = int(teks_item[len(teks_item)-1].replace(",",""))
|
||||
|
||||
uang_diterima = int(self.uang_entry.get())
|
||||
|
||||
# 3. Hitung dan update label
|
||||
kembalian = uang_diterima - total
|
||||
self.kembalian_label.config(text=f"Rp {kembalian:,.0f}")
|
||||
|
||||
except ValueError:
|
||||
# Jika input bukan angka atau kosong, atur kembalian jadi 0
|
||||
self.kembalian_label.config(text="Rp 0")
|
||||
except IndexError:
|
||||
# Jika transaksi tidak dipilih
|
||||
self.kembalian_label.config(text="Rp 0")
|
||||
|
||||
self.uang_entry.bind("<KeyRelease>", hitung_kembalian)
|
||||
|
||||
# =====================
|
||||
# UANG KEMBALIAN (BARU)
|
||||
# =====================
|
||||
kembalian_frame = tk.Frame(self.frame)
|
||||
kembalian_frame.pack(pady=5)
|
||||
|
||||
tk.Label(kembalian_frame, text="Uang Kembalian:").pack(side=tk.LEFT)
|
||||
|
||||
# ⭐️ Label untuk menampilkan kembalian
|
||||
self.kembalian_label = tk.Label(kembalian_frame, text="Rp 0")
|
||||
self.kembalian_label.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
|
||||
# =====================
|
||||
# BUTTON BAYAR
|
||||
# =====================
|
||||
tk.Button(
|
||||
self.frame,
|
||||
text="Proses Pembayaran",
|
||||
bg="#d1e7dd",
|
||||
width=25,
|
||||
command=self.bayar
|
||||
).pack(pady=10)
|
||||
|
||||
self.transaksi_data = []
|
||||
self.load_tagihan()
|
||||
|
||||
# =====================
|
||||
# LOAD TAGIHAN
|
||||
# =====================
|
||||
def load_tagihan(self):
|
||||
self.listbox.delete(0, tk.END)
|
||||
self.transaksi_data = []
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Kasir melihat yang sudah diantar (Served) tapi belum bayar
|
||||
cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Served'")
|
||||
self.data_orders = cur.fetchall()
|
||||
cur.execute("""
|
||||
SELECT id, meja_id, total
|
||||
FROM transaksi
|
||||
WHERE status='Disajikan'
|
||||
""")
|
||||
rows = cur.fetchall()
|
||||
db.close()
|
||||
|
||||
for item in self.data_orders:
|
||||
self.order_list.insert(tk.END, f"ID Transaksi: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3]):,}")
|
||||
for row in rows:
|
||||
t_id, meja, total = row
|
||||
|
||||
self.listbox.insert(
|
||||
tk.END,
|
||||
f"Meja {meja} | ID {t_id} | Total Rp {total:,.0f}"
|
||||
)
|
||||
|
||||
# 🔽 AMBIL DETAIL MENU
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("""
|
||||
SELECT menu_nama, jumlah
|
||||
FROM detail_transaksi
|
||||
WHERE transaksi_id=?
|
||||
""", (t_id,))
|
||||
items = cur.fetchall()
|
||||
db.close()
|
||||
|
||||
for nama, qty in items:
|
||||
self.listbox.insert(
|
||||
tk.END,
|
||||
f" • {nama} x{qty}"
|
||||
)
|
||||
|
||||
self.listbox.insert(tk.END, "-" * 50)
|
||||
self.transaksi_data.append(row)
|
||||
|
||||
|
||||
# =====================
|
||||
# BAYAR
|
||||
# =====================
|
||||
def bayar(self):
|
||||
idx = self.order_list.curselection()
|
||||
idx = self.listbox.curselection()
|
||||
if not idx:
|
||||
messagebox.showwarning("Pilih", "Pilih tagihan dulu!")
|
||||
messagebox.showwarning("Pilih", "Pilih tagihan yang akan dibayar!")
|
||||
return
|
||||
|
||||
selected = self.data_orders[idx[0]]
|
||||
trans_id = selected[0]
|
||||
total = selected[3]
|
||||
idx = self.listbox.curselection()
|
||||
if idx:
|
||||
teks_item = self.listbox.get(idx).split(" ")
|
||||
total = int(teks_item[len(teks_item)-1].replace(",",""))
|
||||
meja = int(teks_item[1])
|
||||
t_id = int(teks_item[4])
|
||||
metode = self.metode.get()
|
||||
|
||||
# Konfirmasi
|
||||
if not messagebox.askyesno("Konfirmasi", f"Terima pembayaran sebesar Rp {int(total):,}?"):
|
||||
if metode == "QRIS":
|
||||
self.tampilkan_qr(t_id, meja, total)
|
||||
|
||||
elif metode == "E-Wallet":
|
||||
self.konfirmasi_ewallet(t_id, meja, total)
|
||||
|
||||
else: # CASH
|
||||
try:
|
||||
uang = int(self.uang_entry.get())
|
||||
except:
|
||||
messagebox.showwarning("Error", "Masukkan uang cash!")
|
||||
return
|
||||
|
||||
if uang < total:
|
||||
messagebox.showwarning(
|
||||
"Kurang",
|
||||
f"Uang kurang!\nTotal: Rp {total:,.0f}"
|
||||
)
|
||||
return
|
||||
|
||||
kembalian = uang - total
|
||||
|
||||
self.selesaikan_pembayaran(
|
||||
t_id, meja, total, metode,
|
||||
uang, kembalian
|
||||
)
|
||||
|
||||
|
||||
# =====================
|
||||
# QRIS (CLOSE WINDOW = LUNAS)
|
||||
# =====================
|
||||
def tampilkan_qr(self, t_id, meja, total):
|
||||
win = tk.Toplevel(self.frame)
|
||||
win.title("QRIS Payment")
|
||||
win.geometry("320x420")
|
||||
|
||||
tk.Label(
|
||||
win,
|
||||
text="SCAN QR UNTUK BAYAR",
|
||||
font=("Arial", 12, "bold")
|
||||
).pack(pady=10)
|
||||
|
||||
qr_data = f"""
|
||||
QRIS-DUMMY
|
||||
Cafe Python
|
||||
Transaksi: {t_id}
|
||||
Meja: {meja}
|
||||
Total: {total}
|
||||
"""
|
||||
|
||||
qr = qrcode.make(qr_data)
|
||||
qr_img = ImageTk.PhotoImage(qr)
|
||||
|
||||
lbl = tk.Label(win, image=qr_img)
|
||||
lbl.image = qr_img
|
||||
lbl.pack(pady=10)
|
||||
|
||||
tk.Label(
|
||||
win,
|
||||
text=f"Total: Rp {total:,.0f}",
|
||||
font=("Arial", 11)
|
||||
).pack(pady=5)
|
||||
|
||||
def selesai_qris():
|
||||
self.selesaikan_pembayaran(t_id, meja, total, "QRIS")
|
||||
win.destroy()
|
||||
|
||||
# ❌ WINDOW DITUTUP = TRANSAKSI BERHASIL
|
||||
win.protocol("WM_DELETE_WINDOW", selesai_qris)
|
||||
|
||||
tk.Button(
|
||||
win,
|
||||
text="Konfirmasi Pembayaran",
|
||||
bg="#d1e7dd",
|
||||
width=25,
|
||||
command=selesai_qris
|
||||
).pack(pady=15)
|
||||
|
||||
# =====================
|
||||
# E-WALLET
|
||||
# =====================
|
||||
def konfirmasi_ewallet(self, t_id, meja, total):
|
||||
confirm = messagebox.askyesno(
|
||||
"E-Wallet Payment",
|
||||
f"""
|
||||
Bayar menggunakan E-Wallet?
|
||||
|
||||
Meja : {meja}
|
||||
Total : Rp {total:,.0f}
|
||||
|
||||
Lanjutkan pembayaran?
|
||||
"""
|
||||
)
|
||||
|
||||
if confirm:
|
||||
self.selesaikan_pembayaran(t_id, meja, total, "E-Wallet")
|
||||
|
||||
def selesaikan_pembayaran(self, t_id, meja, total, metode, uang=None, kembalian=None):
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
|
||||
# Update status jadi 'Paid'
|
||||
cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='CASH' WHERE id=?", (trans_id,))
|
||||
try:
|
||||
cur.execute(
|
||||
"""
|
||||
UPDATE transaksi
|
||||
SET status='Lunas',
|
||||
tanggal = DATETIME('now')
|
||||
WHERE id=?
|
||||
""",
|
||||
(t_id,)
|
||||
)
|
||||
db.commit() # ⭐️ PASTIKAN COMMIT DILAKUKAN
|
||||
tanggal_sekarang = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
sql_query = """
|
||||
INSERT INTO transaksi (tanggal, total, meja_id, status, metode)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
data_transaksi = (tanggal_sekarang, total, meja, "Lunas", metode)
|
||||
cur.execute(sql_query, data_transaksi)
|
||||
db.commit()
|
||||
|
||||
# Tambahkan print debug untuk konfirmasi
|
||||
print(f"DEBUG: Transaksi ID {t_id} berhasil diupdate menjadi Lunas.")
|
||||
|
||||
except Exception as e:
|
||||
# Jika ada error database
|
||||
print(f"ERROR: Gagal update status transaksi: {e}")
|
||||
messagebox.showerror("DB Error", "Gagal memperbarui status transaksi.")
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
|
||||
self.update_data()
|
||||
struk_tambahan = ""
|
||||
if metode == "Cash" and uang is not None and kembalian is not None:
|
||||
struk_tambahan = f"""
|
||||
Uang Diterima: Rp {uang:,.0f}
|
||||
Kembalian : Rp {kembalian:,.0f}"""
|
||||
|
||||
messagebox.showinfo(
|
||||
"Struk Pembayaran",
|
||||
f"""
|
||||
======= STRUK =======
|
||||
ID Transaksi : {t_id}
|
||||
Meja : {meja}
|
||||
Total : Rp {total:,.0f}
|
||||
Metode : {metode}{struk_tambahan}
|
||||
Status : Lunas
|
||||
====================
|
||||
Terima kasih 🙏
|
||||
"""
|
||||
)
|
||||
|
||||
self.load_tagihan()
|
||||
|
||||
def logout(self):
|
||||
self.frame.destroy()
|
||||
from main import LoginScreen
|
||||
LoginScreen(self.parent)
|
||||
|
||||
+21
-36
@@ -1,61 +1,46 @@
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from database import connect
|
||||
|
||||
# Pastikan import ini sesuai
|
||||
from admin_menu import AdminPage
|
||||
from pembeli_menu import PembeliPage
|
||||
from admin_menu import AdminMenu
|
||||
from pembeli_menu import PembeliMenu
|
||||
from kasir import KasirPage
|
||||
from pemilik import PemilikPage
|
||||
from waiter_dashboard import WaiterPage
|
||||
|
||||
class LoginPage(tk.Frame):
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
class LoginPage:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.frame = tk.Frame(root)
|
||||
self.frame.pack(expand=True)
|
||||
|
||||
tk.Label(self, text="LOGIN SYSTEM", font=("Arial", 20, "bold")).pack(pady=40)
|
||||
tk.Label(self.frame, text="LOGIN CAFE", font=("Arial",20,"bold")).pack(pady=10)
|
||||
|
||||
frame_login = tk.Frame(self)
|
||||
frame_login.pack()
|
||||
self.user = tk.Entry(self.frame)
|
||||
self.passw = tk.Entry(self.frame, show="*")
|
||||
self.user.pack()
|
||||
self.passw.pack()
|
||||
|
||||
tk.Label(frame_login, text="Username:").pack()
|
||||
self.user = tk.Entry(frame_login)
|
||||
self.user.pack(pady=5)
|
||||
|
||||
tk.Label(frame_login, text="Password:").pack()
|
||||
self.passw = tk.Entry(frame_login, show="*")
|
||||
self.passw.pack(pady=5)
|
||||
|
||||
tk.Button(frame_login, text="LOGIN", command=self.login, width=20, bg="#2196F3", fg="white").pack(pady=20)
|
||||
tk.Button(self.frame, text="Login", command=self.login).pack(pady=5)
|
||||
|
||||
def login(self):
|
||||
u = self.user.get()
|
||||
p = self.passw.get()
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("SELECT role FROM users WHERE username=? AND password=?", (u, p))
|
||||
cur.execute("SELECT role FROM users WHERE username=? AND password=?",
|
||||
(self.user.get(), self.passw.get()))
|
||||
res = cur.fetchone()
|
||||
db.close()
|
||||
|
||||
if not res:
|
||||
messagebox.showerror("Gagal", "Username/Password salah!")
|
||||
messagebox.showerror("Error","Login gagal")
|
||||
return
|
||||
|
||||
self.frame.destroy()
|
||||
role = res[0]
|
||||
# Reset input
|
||||
self.user.delete(0, tk.END)
|
||||
self.passw.delete(0, tk.END)
|
||||
|
||||
# Redirect sesuai Role
|
||||
if role=="admin":
|
||||
self.controller.show_frame("AdminPage")
|
||||
AdminMenu(self.root)
|
||||
elif role=="pembeli":
|
||||
self.controller.show_frame("PembeliPage")
|
||||
PembeliMenu(self.root)
|
||||
elif role=="kasir":
|
||||
self.controller.show_frame("KasirPage")
|
||||
KasirPage(self.root)
|
||||
elif role=="pemilik":
|
||||
self.controller.show_frame("PemilikPage")
|
||||
elif role == "waiter":
|
||||
self.controller.show_frame("WaiterPage")
|
||||
PemilikPage(self.root)
|
||||
|
||||
+78
-35
@@ -1,44 +1,87 @@
|
||||
import tkinter as tk
|
||||
from database import setup_database
|
||||
|
||||
# Import class yang sudah kita benerin namanya
|
||||
from login import LoginPage
|
||||
from admin_menu import AdminPage
|
||||
from pembeli_menu import PembeliPage
|
||||
from database import setup_database, connect
|
||||
from pembeli_menu import PembeliMenu
|
||||
from kasir import KasirPage
|
||||
from pemilik import PemilikPage
|
||||
from waiter_dashboard import WaiterPage
|
||||
from admin_menu import AdminMenu
|
||||
from waiter_dashboard import WaiterDashboard
|
||||
from tkinter import messagebox
|
||||
|
||||
class CafeApp(tk.Tk):
|
||||
class LoginScreen:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.frame = tk.Frame(root)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
tk.Label(self.frame, text="SISTEM CAFE", font=("Arial", 24, "bold")).pack(pady=30)
|
||||
|
||||
# --- Bagian Login Staff ---
|
||||
frame_login = tk.LabelFrame(self.frame, text="Login Staff (Admin/Kasir/Waiter)", padx=20, pady=20)
|
||||
frame_login.pack(pady=10)
|
||||
|
||||
tk.Label(frame_login, text="Username:").pack(anchor="w")
|
||||
self.username_entry = tk.Entry(frame_login)
|
||||
self.username_entry.pack(fill="x", pady=5)
|
||||
|
||||
tk.Label(frame_login, text="Password:").pack(anchor="w")
|
||||
self.password_entry = tk.Entry(frame_login, show="*")
|
||||
self.password_entry.pack(fill="x", pady=5)
|
||||
|
||||
tk.Button(frame_login, text="Login Staff", bg="#cfe2ff", command=self.login_staff).pack(pady=10, fill="x")
|
||||
|
||||
# --- Bagian Pelanggan (Tanpa Password) ---
|
||||
tk.Label(self.frame, text="--- ATAU ---", fg="grey").pack(pady=5)
|
||||
|
||||
tk.Button(self.frame,
|
||||
text="🍽️ MASUK SEBAGAI PELANGGAN",
|
||||
bg="#d1e7dd", fg="#0f5132", font=("Arial", 12, "bold"),
|
||||
height=2,
|
||||
command=self.masuk_pelanggan).pack(pady=10, ipadx=20)
|
||||
|
||||
def login_staff(self):
|
||||
username = self.username_entry.get()
|
||||
password = self.password_entry.get()
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("SELECT role FROM users WHERE username=? AND password=?", (username, password))
|
||||
result = cur.fetchone()
|
||||
db.close()
|
||||
|
||||
if result:
|
||||
role = result[0]
|
||||
self.frame.destroy()
|
||||
|
||||
if role == "kasir":
|
||||
KasirPage(self.root, self.root)
|
||||
elif role == "pemilik":
|
||||
PemilikPage(self.root, self.root)
|
||||
elif role == "admin":
|
||||
AdminMenu(self.root)
|
||||
elif role == "waiter":
|
||||
WaiterDashboard(self.root)
|
||||
elif role == "pembeli":
|
||||
# Jika ada user iseng login pakai akun pembeli di database
|
||||
PembeliMenu(self.root)
|
||||
else:
|
||||
messagebox.showerror("Error", "Role tidak dikenali")
|
||||
else:
|
||||
messagebox.showerror("Error", "Username / Password salah")
|
||||
|
||||
def masuk_pelanggan(self):
|
||||
# Langsung buka menu tanpa cek password
|
||||
self.frame.destroy()
|
||||
PembeliMenu(self.root)
|
||||
|
||||
# --- App Class ---
|
||||
class App(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title("Sistem Manajemen Kafe Python")
|
||||
self.geometry("1000x700")
|
||||
|
||||
setup_database() # Auto bikin tabel
|
||||
|
||||
self.container = tk.Frame(self)
|
||||
self.container.pack(side="top", fill="both", expand=True)
|
||||
self.container.grid_rowconfigure(0, weight=1)
|
||||
self.container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self.frames = {}
|
||||
|
||||
# Loop semua halaman
|
||||
for F in (LoginPage, AdminPage, PembeliPage, KasirPage, PemilikPage, WaiterPage):
|
||||
page_name = F.__name__
|
||||
frame = F(parent=self.container, controller=self)
|
||||
self.frames[page_name] = frame
|
||||
frame.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
self.show_frame("LoginPage")
|
||||
|
||||
def show_frame(self, page_name):
|
||||
frame = self.frames[page_name]
|
||||
if hasattr(frame, "update_data"):
|
||||
frame.update_data()
|
||||
frame.tkraise()
|
||||
self.title("Sistem Cafe Python")
|
||||
self.geometry("800x600")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = CafeApp()
|
||||
setup_database()
|
||||
app = App()
|
||||
LoginScreen(app)
|
||||
app.mainloop()
|
||||
+187
-117
@@ -1,169 +1,239 @@
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox, ttk
|
||||
from database import connect
|
||||
from PIL import Image, ImageTk
|
||||
import sqlite3
|
||||
import datetime
|
||||
import os
|
||||
|
||||
class PembeliPage(tk.Frame): # Ganti nama class jadi PembeliPage
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
self.cart = {}
|
||||
self.nama_pelanggan = ""
|
||||
self.no_meja = ""
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
self.login_frame = tk.Frame(self)
|
||||
self.menu_frame = tk.Frame(self)
|
||||
self.show_login_ui()
|
||||
# ================= DATABASE CONNECT =================
|
||||
def connect():
|
||||
return sqlite3.connect("cafe.db")
|
||||
|
||||
# --- UI 1: LOGIN PEMBELI ---
|
||||
def show_login_ui(self):
|
||||
self.menu_frame.pack_forget()
|
||||
self.login_frame.pack(fill="both", expand=True)
|
||||
# ================= PEMBELI MENU =================
|
||||
class PembeliMenu:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.frame = tk.Frame(parent)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
for w in self.login_frame.winfo_children(): w.destroy()
|
||||
self.cart = []
|
||||
self.images = []
|
||||
|
||||
tk.Label(self.login_frame, text="SELAMAT DATANG", font=("Arial", 20, "bold")).pack(pady=50)
|
||||
# ================= HEADER =================
|
||||
header = tk.Frame(self.frame)
|
||||
header.pack(fill="x", padx=20, pady=10)
|
||||
|
||||
box = tk.Frame(self.login_frame, bd=2, relief="groove", padx=20, pady=20)
|
||||
box.pack()
|
||||
tk.Label(
|
||||
header,
|
||||
text="SELAMAT DATANG",
|
||||
font=("Arial", 18, "bold")
|
||||
).pack(side="left")
|
||||
|
||||
tk.Label(box, text="Nama Anda:").pack(anchor="w")
|
||||
self.entry_nama = tk.Entry(box, width=30)
|
||||
self.entry_nama.pack(pady=5)
|
||||
tk.Button(
|
||||
header,
|
||||
text="⬅ Kembali",
|
||||
bg="#f8d7da",
|
||||
font=("Arial", 10, "bold"),
|
||||
command=self.logout
|
||||
).pack(side="right")
|
||||
|
||||
tk.Label(box, text="Nomor Meja:").pack(anchor="w")
|
||||
self.combo_meja = ttk.Combobox(box, values=[str(i) for i in range(1, 21)], state="readonly")
|
||||
self.combo_meja.current(0)
|
||||
self.combo_meja.pack(pady=5)
|
||||
|
||||
tk.Button(box, text="MULAI PESAN", bg="#4CAF50", fg="white", command=self.validate_login).pack(pady=20, fill="x")
|
||||
tk.Button(self.login_frame, text="Kembali", command=lambda: self.controller.show_frame("LoginPage")).pack(pady=10)
|
||||
# ================= INPUT DATA =================
|
||||
info_frame = tk.Frame(self.frame, bd=2, relief="groove", padx=10, pady=10)
|
||||
info_frame.pack(pady=5)
|
||||
|
||||
def validate_login(self):
|
||||
if not self.entry_nama.get():
|
||||
messagebox.showwarning("Oops", "Nama wajib diisi!")
|
||||
return
|
||||
self.nama_pelanggan = self.entry_nama.get()
|
||||
self.no_meja = self.combo_meja.get()
|
||||
self.show_menu_ui()
|
||||
tk.Label(info_frame, text="Nama Anda:").grid(row=0, column=0, sticky="e")
|
||||
self.entry_nama = tk.Entry(info_frame)
|
||||
self.entry_nama.grid(row=0, column=1, padx=5)
|
||||
|
||||
# --- UI 2: MENU MAKANAN ---
|
||||
def show_menu_ui(self):
|
||||
self.login_frame.pack_forget()
|
||||
self.menu_frame.pack(fill="both", expand=True)
|
||||
tk.Label(info_frame, text="Pilih Meja:").grid(row=0, column=2, sticky="e")
|
||||
self.meja_var = tk.StringVar()
|
||||
self.combo_meja = ttk.Combobox(
|
||||
info_frame,
|
||||
textvariable=self.meja_var,
|
||||
state="readonly",
|
||||
width=5
|
||||
)
|
||||
self.combo_meja.grid(row=0, column=3, padx=5)
|
||||
|
||||
# Bersihkan frame lama biar gak numpuk
|
||||
for w in self.menu_frame.winfo_children(): w.destroy()
|
||||
tk.Button(info_frame, text="🔄", command=self.refresh_available_tables).grid(row=0, column=4, padx=2)
|
||||
|
||||
# Header
|
||||
top = tk.Frame(self.menu_frame, bg="#eee", pady=10)
|
||||
top.pack(fill="x")
|
||||
tk.Label(top, text=f"👤 {self.nama_pelanggan} | Meja {self.no_meja}", font=("Arial", 12)).pack(side="left", padx=20)
|
||||
tk.Button(top, text="Keluar", bg="#ff6b6b", fg="white", command=self.logout).pack(side="right", padx=20)
|
||||
self.refresh_available_tables()
|
||||
|
||||
content = tk.Frame(self.menu_frame)
|
||||
content.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
# ================= CONTENT 2 KOLOM =================
|
||||
content = tk.Frame(self.frame)
|
||||
content.pack(fill="both", expand=True, padx=20, pady=10)
|
||||
|
||||
# Kiri: Daftar Menu
|
||||
left_frame = tk.LabelFrame(content, text=" Menu Tersedia ")
|
||||
left_frame.pack(side="left", fill="both", expand=True)
|
||||
content.grid_columnconfigure(0, weight=3)
|
||||
content.grid_columnconfigure(1, weight=1)
|
||||
|
||||
canvas = tk.Canvas(left_frame)
|
||||
scrollbar = tk.Scrollbar(left_frame, orient="vertical", command=canvas.yview)
|
||||
self.scroll_frame = tk.Frame(canvas)
|
||||
# ================= MENU (KIRI) =================
|
||||
self.menu_frame = tk.Frame(content)
|
||||
self.menu_frame.grid(row=0, column=0, sticky="nw")
|
||||
self.load_menu()
|
||||
|
||||
self.scroll_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
|
||||
canvas.create_window((0, 0), window=self.scroll_frame, anchor="nw")
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
# ================= KERANJANG (KANAN) =================
|
||||
cart_frame = tk.LabelFrame(
|
||||
content,
|
||||
text="Keranjang Pesanan",
|
||||
font=("Arial", 11, "bold"),
|
||||
padx=10,
|
||||
pady=10
|
||||
)
|
||||
cart_frame.grid(row=0, column=1, sticky="ne")
|
||||
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
self.listbox = tk.Listbox(cart_frame, width=35, height=10, font=("Courier", 10))
|
||||
self.listbox.pack(pady=5)
|
||||
|
||||
# Kanan: Keranjang
|
||||
right_frame = tk.LabelFrame(content, text=" Keranjang ")
|
||||
right_frame.pack(side="right", fill="both", width=300, padx=5)
|
||||
tk.Button(cart_frame, text="➖ Kurangi / Hapus Item", fg="red",
|
||||
command=self.kurangi_item_cart).pack(pady=5)
|
||||
|
||||
self.cart_list = tk.Listbox(right_frame)
|
||||
self.cart_list.pack(fill="both", expand=True, padx=5, pady=5)
|
||||
self.total_lbl = tk.Label(cart_frame, text="Total: Rp 0",
|
||||
font=("Arial", 12, "bold"))
|
||||
self.total_lbl.pack(pady=5)
|
||||
|
||||
self.lbl_total = tk.Label(right_frame, text="Total: Rp 0", font=("Arial", 14, "bold"), fg="red")
|
||||
self.lbl_total.pack(pady=5)
|
||||
|
||||
tk.Button(right_frame, text="PESAN SEKARANG", bg="green", fg="white", height=2, command=self.checkout).pack(fill="x", padx=5, pady=5)
|
||||
|
||||
self.load_menu_items()
|
||||
|
||||
def load_menu_items(self):
|
||||
for w in self.scroll_frame.winfo_children(): w.destroy()
|
||||
tk.Button(cart_frame, text="✅ KIRIM PESANAN", bg="#d1e7dd",
|
||||
font=("Arial", 11, "bold"), command=self.checkout).pack(pady=10)
|
||||
|
||||
# ================= MEJA =================
|
||||
def refresh_available_tables(self):
|
||||
total_meja = [str(i) for i in range(1, 11)]
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("SELECT id, nama, harga, stok FROM menu WHERE stok > 0")
|
||||
items = cur.fetchall()
|
||||
cur.execute("SELECT DISTINCT meja_id FROM transaksi WHERE status != 'Selesai'")
|
||||
terisi = [str(row[0]) for row in cur.fetchall()]
|
||||
db.close()
|
||||
|
||||
row, col = 0, 0
|
||||
for item in items:
|
||||
m_id, nama, harga, stok = item
|
||||
card = tk.Frame(self.scroll_frame, bd=2, relief="ridge", padx=5, pady=5)
|
||||
card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
|
||||
tersedia = [m for m in total_meja if m not in terisi]
|
||||
self.combo_meja['values'] = tersedia if tersedia else ["Penuh"]
|
||||
self.combo_meja.set(tersedia[0] if tersedia else "Penuh")
|
||||
|
||||
tk.Label(card, text=nama, font=("Arial", 10, "bold")).pack()
|
||||
tk.Label(card, text=f"Rp {int(harga):,}", fg="green").pack()
|
||||
tk.Button(card, text="Tambah +", command=lambda i=m_id, n=nama, h=harga: self.add_to_cart(i, n, h)).pack(pady=2)
|
||||
# ================= MENU =================
|
||||
def load_menu(self):
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("SELECT nama, harga, gambar FROM menu")
|
||||
data = cur.fetchall()
|
||||
db.close()
|
||||
|
||||
col += 1
|
||||
if col > 2:
|
||||
col = 0
|
||||
row += 1
|
||||
columns = 4
|
||||
for i, (nama, harga, gambar) in enumerate(data):
|
||||
row, col = divmod(i, columns)
|
||||
card = tk.Frame(self.menu_frame, width=180, height=220, bd=2, relief="ridge")
|
||||
card.grid(row=row, column=col, padx=10, pady=10)
|
||||
card.grid_propagate(False)
|
||||
|
||||
def add_to_cart(self, m_id, nama, harga):
|
||||
if m_id in self.cart:
|
||||
self.cart[m_id]['qty'] += 1
|
||||
try:
|
||||
if not gambar:
|
||||
raise ValueError("gambar kosong")
|
||||
|
||||
# gambar dari DB sudah mengandung 'aset/...'
|
||||
img_path = os.path.join(BASE_DIR, gambar)
|
||||
|
||||
if not os.path.exists(img_path):
|
||||
raise FileNotFoundError(img_path)
|
||||
|
||||
img = Image.open(img_path)
|
||||
img = img.resize((140, 90), Image.Resampling.LANCZOS)
|
||||
photo = ImageTk.PhotoImage(img)
|
||||
|
||||
self.images.append(photo)
|
||||
tk.Label(card, image=photo).pack(pady=5)
|
||||
|
||||
except Exception as e:
|
||||
print("IMG ERROR:", e)
|
||||
tk.Label(card, text="No Image", height=6).pack()
|
||||
|
||||
|
||||
tk.Label(card, text=nama, font=("Arial", 10, "bold"), wraplength=160).pack()
|
||||
tk.Label(card, text=f"Rp {harga:,.0f}").pack(pady=2)
|
||||
|
||||
tk.Button(card, text="Tambah", bg="#cfe2ff",
|
||||
command=lambda n=nama, h=harga: self.add_to_cart(n, h)).pack(pady=5)
|
||||
|
||||
# ================= CART =================
|
||||
def add_to_cart(self, nama, harga):
|
||||
for item in self.cart:
|
||||
if item['nama'] == nama:
|
||||
item['qty'] += 1
|
||||
item['subtotal'] = item['qty'] * harga
|
||||
self.refresh_cart()
|
||||
return
|
||||
self.cart.append({'nama': nama, 'harga_satuan': harga, 'qty': 1, 'subtotal': harga})
|
||||
self.refresh_cart()
|
||||
|
||||
def kurangi_item_cart(self):
|
||||
if not self.listbox.curselection(): return
|
||||
idx = self.listbox.curselection()[0]
|
||||
item = self.cart[idx]
|
||||
item['qty'] -= 1
|
||||
if item['qty'] <= 0:
|
||||
self.cart.pop(idx)
|
||||
else:
|
||||
self.cart[m_id] = {'nama': nama, 'harga': harga, 'qty': 1}
|
||||
item['subtotal'] = item['qty'] * item['harga_satuan']
|
||||
self.refresh_cart()
|
||||
|
||||
def refresh_cart(self):
|
||||
self.cart_list.delete(0, tk.END)
|
||||
self.listbox.delete(0, tk.END)
|
||||
total = 0
|
||||
for m_id, item in self.cart.items():
|
||||
sub = item['harga'] * item['qty']
|
||||
total += sub
|
||||
self.cart_list.insert(tk.END, f"{item['qty']}x {item['nama']} = {sub:,}")
|
||||
self.lbl_total.config(text=f"Total: Rp {int(total):,}")
|
||||
for item in self.cart:
|
||||
self.listbox.insert(tk.END, f"{item['nama']} (x{item['qty']}) - Rp {item['subtotal']:,.0f}")
|
||||
total += item['subtotal']
|
||||
self.total_lbl.config(text=f"Total: Rp {total:,.0f}")
|
||||
|
||||
def checkout(self):
|
||||
if not self.cart: return
|
||||
if not self.entry_nama.get() or not self.cart:
|
||||
messagebox.showwarning("Error", "Data belum lengkap")
|
||||
return
|
||||
|
||||
total_val = int(self.lbl_total.cget("text").replace("Total: Rp ","").replace(",",""))
|
||||
total = sum(item['subtotal'] for item in self.cart)
|
||||
tanggal = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
|
||||
# Simpan ke tabel TRANSAKSI (Bukan orders)
|
||||
cur.execute("INSERT INTO transaksi (nama_pelanggan, meja_id, total, status) VALUES (?, ?, ?, 'Pending')",
|
||||
(self.nama_pelanggan, self.no_meja, total_val))
|
||||
trans_id = cur.lastrowid
|
||||
# 1️⃣ Simpan transaksi
|
||||
cur.execute("""
|
||||
INSERT INTO transaksi (tanggal, total, meja_id, status)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""", (tanggal, total, self.combo_meja.get(), "Pending"))
|
||||
|
||||
# Simpan Detail
|
||||
for m_id, item in self.cart.items():
|
||||
sub = item['harga'] * item['qty']
|
||||
cur.execute("INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal) VALUES (?,?,?,?)",
|
||||
(trans_id, m_id, item['qty'], sub))
|
||||
# Kurangi Stok
|
||||
cur.execute("UPDATE menu SET stok = stok - ? WHERE id=?", (item['qty'], m_id))
|
||||
transaksi_id = cur.lastrowid # 🔥 AMBIL ID TRANSAKSI
|
||||
|
||||
# 2️⃣ Simpan detail menu
|
||||
for item in self.cart:
|
||||
cur.execute("""
|
||||
INSERT INTO detail_transaksi
|
||||
(transaksi_id, menu_nama, harga, jumlah, subtotal)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (
|
||||
transaksi_id,
|
||||
item['nama'],
|
||||
item['harga_satuan'],
|
||||
item['qty'],
|
||||
item['subtotal']
|
||||
))
|
||||
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
messagebox.showinfo("Sukses", "Pesanan terkirim! Mohon tunggu.")
|
||||
self.cart = {}
|
||||
messagebox.showinfo("Berhasil", "Pesanan berhasil dikirim")
|
||||
self.cart.clear()
|
||||
self.refresh_cart()
|
||||
self.load_menu_items()
|
||||
|
||||
|
||||
def logout(self):
|
||||
self.cart = {}
|
||||
self.show_login_ui()
|
||||
self.frame.destroy()
|
||||
from main import LoginScreen
|
||||
LoginScreen(self.parent)
|
||||
|
||||
def update_data(self):
|
||||
self.show_login_ui()
|
||||
|
||||
# ================= RUN =================
|
||||
if __name__ == '__main__':
|
||||
root = tk.Tk()
|
||||
root.title("Sistem Cafe Python")
|
||||
root.geometry("1200x700")
|
||||
PembeliMenu(root)
|
||||
root.mainloop()
|
||||
+166
-20
@@ -1,36 +1,182 @@
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from database import connect
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
class PemilikPage(tk.Frame): # HARUS inherit tk.Frame
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent) # Init parent class
|
||||
|
||||
class PemilikPage:
|
||||
def __init__(self, parent, controller=None):
|
||||
self.parent = parent
|
||||
self.controller = controller
|
||||
|
||||
tk.Label(self, text="LAPORAN PENJUALAN (OWNER)", font=("Arial", 18, "bold")).pack(pady=20)
|
||||
self.frame = tk.Frame(parent)
|
||||
self.frame.pack(fill="both", expand=True)
|
||||
|
||||
self.box = tk.Frame(self, bd=2, relief="groove", padx=40, pady=40)
|
||||
self.box.pack()
|
||||
# =====================
|
||||
# JUDUL
|
||||
# =====================
|
||||
tk.Label(
|
||||
self.frame,
|
||||
text="LAPORAN PENJUALAN",
|
||||
font=("Arial", 18, "bold")
|
||||
).pack(pady=10)
|
||||
|
||||
# Label Total
|
||||
self.total_lbl = tk.Label(self.box, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
|
||||
# =====================
|
||||
# FILTER PERIODE
|
||||
# =====================
|
||||
filter_frame = tk.Frame(self.frame)
|
||||
filter_frame.pack(pady=5)
|
||||
|
||||
tk.Label(filter_frame, text="Periode:").pack(side=tk.LEFT)
|
||||
|
||||
self.filter_var = tk.StringVar(value="Harian")
|
||||
tk.OptionMenu(
|
||||
filter_frame,
|
||||
self.filter_var,
|
||||
"Harian", "Mingguan", "Bulanan"
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
tk.Button(
|
||||
filter_frame,
|
||||
text="Refresh",
|
||||
bg="#cfe2ff",
|
||||
command=self.load
|
||||
).pack(side=tk.LEFT, padx=10)
|
||||
|
||||
# =====================
|
||||
# TOTAL
|
||||
# =====================
|
||||
self.total_lbl = tk.Label(
|
||||
self.frame,
|
||||
text="Total (Harian): Rp 0",
|
||||
font=("Arial", 14, "bold")
|
||||
)
|
||||
self.total_lbl.pack(pady=10)
|
||||
|
||||
self.count_lbl = tk.Label(self.box, text="Jumlah Transaksi: 0", font=("Arial", 12))
|
||||
self.count_lbl.pack(pady=5)
|
||||
# =====================
|
||||
# GRAFIK
|
||||
# =====================
|
||||
tk.Button(
|
||||
self.frame,
|
||||
text="Tampilkan Grafik",
|
||||
bg="#d1e7dd",
|
||||
width=20,
|
||||
command=self.show_graph
|
||||
).pack(pady=5)
|
||||
|
||||
tk.Button(self, text="Refresh Laporan", bg="#cfe2ff", command=self.update_data).pack(pady=10)
|
||||
tk.Button(self, text="Logout", bg="#f9e79f", command=lambda: controller.show_frame("LoginPage")).pack(pady=5)
|
||||
# =====================
|
||||
# LOGOUT
|
||||
# =====================
|
||||
tk.Button(
|
||||
self.frame,
|
||||
text="Logout",
|
||||
bg="#f9e79f",
|
||||
command=self.logout
|
||||
).pack(pady=5)
|
||||
|
||||
def update_data(self):
|
||||
self.load()
|
||||
|
||||
# =====================
|
||||
# LOAD TOTAL (SUM ASLI)
|
||||
# =====================
|
||||
def load(self):
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Hitung total dari transaksi yang statusnya 'Paid'
|
||||
cur.execute("SELECT SUM(total), COUNT(id) FROM transaksi WHERE status='Paid'")
|
||||
res = cur.fetchone()
|
||||
periode = self.filter_var.get()
|
||||
|
||||
if periode == "Harian":
|
||||
cur.execute("""
|
||||
SELECT COALESCE(SUM(total), 0)
|
||||
FROM transaksi
|
||||
WHERE status='Lunas'
|
||||
AND datetime(tanggal) >= datetime('now','-1 day')
|
||||
""")
|
||||
|
||||
elif periode == "Mingguan":
|
||||
cur.execute("""
|
||||
SELECT COALESCE(SUM(total), 0)
|
||||
FROM transaksi
|
||||
WHERE status='Lunas'
|
||||
AND datetime(tanggal) >= datetime('now','-7 day')
|
||||
""")
|
||||
|
||||
else: # Bulanan
|
||||
cur.execute("""
|
||||
SELECT COALESCE(SUM(total), 0)
|
||||
FROM transaksi
|
||||
WHERE status='Lunas'
|
||||
AND strftime('%Y-%m', tanggal) = strftime('%Y-%m','now')
|
||||
""")
|
||||
|
||||
total = cur.fetchone()[0]
|
||||
db.close()
|
||||
|
||||
total_duit = res[0] if res[0] else 0
|
||||
total_transaksi = res[1] if res[1] else 0
|
||||
self.total_lbl.config(
|
||||
text=f"Total ({periode}): Rp {total:,.0f}"
|
||||
)
|
||||
|
||||
self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total_duit):,}")
|
||||
self.count_lbl.config(text=f"Jumlah Transaksi Sukses: {total_transaksi}")
|
||||
# =====================
|
||||
# GRAFIK (SUM SESUAI TOTAL)
|
||||
# =====================
|
||||
def show_graph(self):
|
||||
plt.close('all')
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
periode = self.filter_var.get()
|
||||
|
||||
if periode == "Harian":
|
||||
cur.execute("""
|
||||
SELECT strftime('%H:%M', tanggal), SUM(total)
|
||||
FROM transaksi
|
||||
WHERE status='Lunas'
|
||||
AND datetime(tanggal) >= datetime('now','-1 day')
|
||||
GROUP BY strftime('%H', tanggal)
|
||||
ORDER BY tanggal
|
||||
""")
|
||||
|
||||
elif periode == "Mingguan":
|
||||
cur.execute("""
|
||||
SELECT date(tanggal), SUM(total)
|
||||
FROM transaksi
|
||||
WHERE status='Lunas'
|
||||
AND datetime(tanggal) >= datetime('now','-7 day')
|
||||
GROUP BY date(tanggal)
|
||||
ORDER BY date(tanggal)
|
||||
""")
|
||||
|
||||
else:
|
||||
cur.execute("""
|
||||
SELECT strftime('%Y-%m', tanggal), SUM(total)
|
||||
FROM transaksi
|
||||
WHERE status='Lunas'
|
||||
GROUP BY strftime('%Y-%m', tanggal)
|
||||
ORDER BY strftime('%Y-%m', tanggal)
|
||||
""")
|
||||
|
||||
data = cur.fetchall()
|
||||
db.close()
|
||||
|
||||
if not data:
|
||||
messagebox.showinfo("Info", "Belum ada data penjualan")
|
||||
return
|
||||
|
||||
labels = [d[0] for d in data]
|
||||
totals = [d[1] for d in data]
|
||||
|
||||
plt.figure()
|
||||
plt.plot(labels, totals, marker='o')
|
||||
plt.title(f"Grafik Penjualan ({periode})")
|
||||
plt.xlabel("Periode")
|
||||
plt.ylabel("Total Penjualan")
|
||||
plt.xticks(rotation=45)
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
# =====================
|
||||
# LOGOUT
|
||||
# =====================
|
||||
def logout(self):
|
||||
self.frame.destroy()
|
||||
from main import LoginScreen
|
||||
LoginScreen(self.parent)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
project/
|
||||
│ main.py
|
||||
│ database.py
|
||||
│ pembeli_menu.py
|
||||
│ kasir_page.py
|
||||
│ pemilik_page.py
|
||||
│ mie ayam.webp
|
||||
│ mie kuah.webp
|
||||
│ es teh.webp
|
||||
│ jus jeruk.webp
|
||||
+104
-35
@@ -2,58 +2,127 @@ import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from database import connect
|
||||
|
||||
class WaiterPage(tk.Frame):
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
class WaiterDashboard:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.frame = tk.Frame(parent)
|
||||
self.frame.pack(fill="both", expand=True, padx=20, pady=10)
|
||||
|
||||
# Header
|
||||
top = tk.Frame(self, bg="#FF9800")
|
||||
top.pack(fill="x")
|
||||
tk.Label(top, text="WAITER DASHBOARD", font=("Arial", 16, "bold"), bg="#FF9800", fg="white").pack(side="left", padx=10, pady=10)
|
||||
tk.Button(top, text="Logout", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10)
|
||||
# =====================
|
||||
# HEADER
|
||||
# =====================
|
||||
tk.Label(
|
||||
self.frame,
|
||||
text="DASHBOARD WAITER (DAPUR)",
|
||||
font=("Arial", 18, "bold")
|
||||
).pack(pady=10)
|
||||
|
||||
# Content
|
||||
tk.Label(self, text="Pesanan Pending (Perlu Diantar):", font=("Arial", 12)).pack(pady=10)
|
||||
top_btn = tk.Frame(self.frame)
|
||||
top_btn.pack(pady=5)
|
||||
|
||||
self.list_orders = tk.Listbox(self, width=80, height=15)
|
||||
self.list_orders.pack(pady=5)
|
||||
tk.Button(top_btn, text="Refresh Data", bg="#cfe2ff",
|
||||
width=15, command=self.load_orders).grid(row=0, column=0, padx=5)
|
||||
|
||||
btn_frame = tk.Frame(self)
|
||||
btn_frame.pack(pady=10)
|
||||
tk.Button(top_btn, text="Logout", bg="#f8d7da",
|
||||
width=10, command=self.logout).grid(row=0, column=1, padx=5)
|
||||
|
||||
tk.Button(btn_frame, text="Refresh", command=self.update_data).pack(side="left", padx=5)
|
||||
tk.Button(btn_frame, text="✅ Selesai Diantar (Served)", bg="#81C784", command=self.mark_served).pack(side="left", padx=5)
|
||||
# =====================
|
||||
# LIST PESANAN + SCROLL
|
||||
# =====================
|
||||
list_frame = tk.Frame(self.frame)
|
||||
list_frame.pack(pady=10)
|
||||
|
||||
def update_data(self):
|
||||
self.list_orders.delete(0, tk.END)
|
||||
self.data_orders = []
|
||||
scrollbar = tk.Scrollbar(list_frame)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
self.listbox = tk.Listbox(
|
||||
list_frame,
|
||||
width=90,
|
||||
height=15,
|
||||
yscrollcommand=scrollbar.set
|
||||
)
|
||||
self.listbox.pack(side=tk.LEFT)
|
||||
|
||||
scrollbar.config(command=self.listbox.yview)
|
||||
|
||||
# =====================
|
||||
# BUTTON VALIDASI
|
||||
# =====================
|
||||
tk.Button(
|
||||
self.frame,
|
||||
text="✔ Pesanan Selesai Disajikan",
|
||||
bg="#d1e7dd",
|
||||
font=("Arial", 11, "bold"),
|
||||
width=30,
|
||||
command=self.validasi_layanan
|
||||
).pack(pady=10)
|
||||
|
||||
self.order_ids = []
|
||||
self.load_orders()
|
||||
|
||||
# =====================
|
||||
# LOAD DATA
|
||||
# =====================
|
||||
def load_orders(self):
|
||||
self.listbox.delete(0, tk.END)
|
||||
self.order_ids = []
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Ambil transaksi yang statusnya 'Pending'
|
||||
cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Pending'")
|
||||
self.data_orders = cur.fetchall()
|
||||
|
||||
cur.execute(
|
||||
"SELECT id, meja_id, total FROM transaksi WHERE status='Pending'"
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
|
||||
for t_id, meja, total in rows:
|
||||
cur.execute(
|
||||
"SELECT menu_nama, jumlah FROM detail_transaksi WHERE transaksi_id=?",
|
||||
(t_id,)
|
||||
)
|
||||
details = cur.fetchall()
|
||||
|
||||
self.listbox.insert(tk.END, f"Meja {meja} | ID Transaksi: {t_id}")
|
||||
for menu, qty in details:
|
||||
self.listbox.insert(tk.END, f" - {menu} x{qty}")
|
||||
|
||||
self.listbox.insert(tk.END, f"Total : Rp {total:,.0f}")
|
||||
self.listbox.insert(tk.END, "-" * 70)
|
||||
|
||||
self.order_ids.append(t_id)
|
||||
|
||||
db.close()
|
||||
|
||||
for item in self.data_orders:
|
||||
# item = (id, nama, meja, total)
|
||||
self.list_orders.insert(tk.END, f"ID: {item[0]} | Meja: {item[2]} | A.N: {item[1]} | Total: Rp {int(item[3]):,}")
|
||||
|
||||
def mark_served(self):
|
||||
idx = self.list_orders.curselection()
|
||||
# =====================
|
||||
# VALIDASI
|
||||
# =====================
|
||||
def validasi_layanan(self):
|
||||
idx = self.listbox.curselection()
|
||||
if not idx:
|
||||
messagebox.showwarning("Pilih", "Pilih pesanan dulu!")
|
||||
messagebox.showwarning("Pilih", "Pilih pesanan yang sudah disajikan")
|
||||
return
|
||||
|
||||
trans_id = self.data_orders[idx[0]][0]
|
||||
# Cari ID terdekat ke atas
|
||||
selected_index = idx[0]
|
||||
transaksi_index = selected_index // 5
|
||||
transaksi_id = self.order_ids[transaksi_index]
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Update status jadi 'Served' biar muncul di Kasir
|
||||
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (trans_id,))
|
||||
cur.execute(
|
||||
"UPDATE transaksi SET status='Disajikan' WHERE id=?",
|
||||
(transaksi_id,)
|
||||
)
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
messagebox.showinfo("Sukses", "Pesanan Meja ini sudah dilayani!")
|
||||
self.update_data()
|
||||
messagebox.showinfo(
|
||||
"Sukses",
|
||||
"Pesanan ditandai selesai.\nKasir bisa memproses pembayaran."
|
||||
)
|
||||
self.load_orders()
|
||||
|
||||
def logout(self):
|
||||
self.frame.destroy()
|
||||
from main import LoginScreen
|
||||
LoginScreen(self.parent)
|
||||
|
||||
Reference in New Issue
Block a user