Compare commits
No commits in common. "2bc30165e2d8654de91438542c7dc55ab4404d85" and "1bb52206cc86eff84b49514662e3d22255045f54" have entirely different histories.
2bc30165e2
...
1bb52206cc
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.
@ -4,21 +4,27 @@ from database import connect
|
||||
import os
|
||||
import shutil
|
||||
|
||||
class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
|
||||
# GANTI NAMA CLASS JADI AdminPage
|
||||
class AdminPage(tk.Frame):
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
self.selected_image_path = None
|
||||
self.data_menu = []
|
||||
|
||||
# --- 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)
|
||||
tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10)
|
||||
|
||||
# --- Form Input (Kiri) ---
|
||||
left_frame = tk.Frame(self, padx=10, pady=10)
|
||||
left_frame.pack(side="left", fill="y")
|
||||
# --- Layout Kiri (Form) & Kanan (Tabel) ---
|
||||
main_content = tk.Frame(self)
|
||||
main_content.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
# --- KIRI ---
|
||||
left_frame = tk.Frame(main_content)
|
||||
left_frame.pack(side="left", fill="y", padx=10)
|
||||
|
||||
tk.Label(left_frame, text="Nama Menu:").pack(anchor="w")
|
||||
self.entry_nama = tk.Entry(left_frame, width=30)
|
||||
@ -30,6 +36,7 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
|
||||
|
||||
tk.Label(left_frame, text="Stok Awal:").pack(anchor="w")
|
||||
self.entry_stok = tk.Entry(left_frame, width=30)
|
||||
self.entry_stok.insert(0, "100") # Default stok
|
||||
self.entry_stok.pack(pady=5)
|
||||
|
||||
tk.Label(left_frame, text="Gambar:").pack(anchor="w")
|
||||
@ -38,17 +45,16 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
|
||||
self.lbl_img_path = tk.Label(left_frame, text="Belum ada gambar", fg="gray", font=("Arial", 8))
|
||||
self.lbl_img_path.pack(anchor="w")
|
||||
|
||||
# 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)
|
||||
|
||||
# --- Tabel Menu (Kanan) ---
|
||||
right_frame = tk.Frame(self, padx=10, pady=10)
|
||||
# --- KANAN ---
|
||||
right_frame = tk.Frame(main_content)
|
||||
right_frame.pack(side="right", fill="both", expand=True)
|
||||
|
||||
tk.Label(right_frame, text="Daftar Menu Database:", font=("Arial", 10, "bold")).pack(anchor="w")
|
||||
tk.Label(right_frame, text="Daftar Menu:", font=("Arial", 10, "bold")).pack(anchor="w")
|
||||
|
||||
self.list_menu = tk.Listbox(right_frame)
|
||||
self.list_menu.pack(fill="both", expand=True)
|
||||
@ -73,7 +79,7 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
|
||||
self.list_menu.insert(tk.END, f"{row[1]} - Rp {int(row[2])} (Stok: {row[3]})")
|
||||
|
||||
def browse_image(self):
|
||||
filename = filedialog.askopenfilename(title="Pilih Gambar", filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")])
|
||||
filename = filedialog.askopenfilename(title="Pilih Gambar", filetypes=[("Images", "*.png;*.jpg;*.jpeg")])
|
||||
if filename:
|
||||
self.selected_image_path = filename
|
||||
self.lbl_img_path.config(text=os.path.basename(filename), fg="black")
|
||||
@ -90,21 +96,20 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
|
||||
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
|
||||
destinasi = os.path.join("img", os.path.basename(self.selected_image_path))
|
||||
try:
|
||||
shutil.copy(self.selected_image_path, destinasi)
|
||||
final_image_path = destinasi
|
||||
except:
|
||||
pass
|
||||
|
||||
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))
|
||||
(nama, harga, stok, 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))
|
||||
@ -113,14 +118,12 @@ class AdminPage(tk.Frame): # Ganti nama class jadi AdminPage
|
||||
|
||||
def delete_menu(self):
|
||||
idx = self.list_menu.curselection()
|
||||
if not idx:
|
||||
messagebox.showwarning("Pilih", "Pilih menu dulu!")
|
||||
return
|
||||
if not idx: return
|
||||
|
||||
id_menu = self.data_menu[idx[0]][0]
|
||||
if messagebox.askyesno("Hapus", "Yakin hapus menu ini?"):
|
||||
selected = self.data_menu[idx[0]]
|
||||
if messagebox.askyesno("Hapus", f"Hapus {selected[1]}?"):
|
||||
db = connect()
|
||||
db.cursor().execute("DELETE FROM menu WHERE id=?", (id_menu,))
|
||||
db.cursor().execute("DELETE FROM menu WHERE id=?", (selected[0],))
|
||||
db.commit()
|
||||
db.close()
|
||||
self.update_data()
|
||||
@ -1,3 +1,4 @@
|
||||
# File: database.py
|
||||
import sqlite3
|
||||
|
||||
def connect():
|
||||
@ -29,7 +30,7 @@ def setup_database():
|
||||
)
|
||||
""")
|
||||
|
||||
# 3. Transaksi (Header)
|
||||
# 3. Transaksi
|
||||
cur.execute("""
|
||||
CREATE TABLE IF NOT EXISTS transaksi(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@ -41,7 +42,6 @@ def setup_database():
|
||||
status TEXT DEFAULT 'Pending'
|
||||
)
|
||||
""")
|
||||
# Status Flow: 'Pending' (Dipesan) -> 'Served' (Diantar Waiter) -> 'Paid' (Dibayar di Kasir)
|
||||
|
||||
# 4. Detail Transaksi
|
||||
cur.execute("""
|
||||
@ -54,7 +54,7 @@ def setup_database():
|
||||
)
|
||||
""")
|
||||
|
||||
# Seeding Data User Dummy
|
||||
# Seeding User (Kalau kosong diisi default)
|
||||
cur.execute("SELECT COUNT(*) FROM users")
|
||||
if cur.fetchone()[0] == 0:
|
||||
users = [
|
||||
@ -68,7 +68,7 @@ def setup_database():
|
||||
|
||||
db.commit()
|
||||
db.close()
|
||||
print("Database Ready bos!")
|
||||
print("Database Ready!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_database()
|
||||
@ -1,49 +1,32 @@
|
||||
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)
|
||||
conn = sqlite3.connect("cafe.db")
|
||||
cur = conn.cursor()
|
||||
|
||||
print("♻️ Membersihkan data lama...")
|
||||
# Hapus data lama biar gak dobel (opsional, biar bersih)
|
||||
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/')
|
||||
# Data Menu (Sesuaikan nama file gambar dengan yang ada di folder aset kamu)
|
||||
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"),
|
||||
# (Nama, Kategori, Harga, Stok, Nama File Gambar)
|
||||
("Ayam Goreng", "Makanan", 15000, 20, "ayam_goreng.jpg"),
|
||||
("Bakso Urat", "Makanan", 12000, 15, "bakso.jpg"),
|
||||
("Mie Ayam", "Makanan", 10000, 25, "mie_ayam.jpg"),
|
||||
("Es Teh Manis", "Minuman", 3000, 50, "es_teh.jpg"),
|
||||
("Jus Jeruk", "Minuman", 5000, 30, "jus_jeruk.jpg"),
|
||||
]
|
||||
|
||||
print("📥 Sedang mengisi data menu...")
|
||||
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()
|
||||
cur.executemany("""
|
||||
INSERT INTO menu (nama, kategori, harga, stok, gambar)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", menus)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print("✅ Berhasil! Data menu sudah masuk database.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
isi_data_awal()
|
||||
@ -13,13 +13,13 @@ class KasirPage(tk.Frame):
|
||||
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)
|
||||
|
||||
# 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)
|
||||
# List Order
|
||||
tk.Label(self, text="Tagihan Belum Lunas (Status: Served):", font=("Arial", 11)).pack(pady=10)
|
||||
self.order_list = tk.Listbox(self, width=80, height=15, font=("Arial", 10))
|
||||
self.order_list.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, text="💰 PROSES BAYAR (LUNAS)", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=50, pady=20)
|
||||
|
||||
def update_data(self):
|
||||
self.order_list.delete(0, tk.END)
|
||||
@ -27,36 +27,35 @@ class KasirPage(tk.Frame):
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Kasir melihat yang sudah diantar (Served) tapi belum bayar
|
||||
# Kasir melihat yang statusnya Served
|
||||
cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Served'")
|
||||
self.data_orders = 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]):,}")
|
||||
self.order_list.insert(tk.END, f"ID: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3])}")
|
||||
|
||||
def bayar(self):
|
||||
idx = self.order_list.curselection()
|
||||
if not idx:
|
||||
messagebox.showwarning("Pilih", "Pilih tagihan dulu!")
|
||||
messagebox.showwarning("Pilih", "Pilih tagihan yang mau dibayar!")
|
||||
return
|
||||
|
||||
selected = self.data_orders[idx[0]]
|
||||
trans_id = selected[0]
|
||||
transaksi_id = selected[0]
|
||||
nama = selected[1]
|
||||
total = selected[3]
|
||||
|
||||
# Konfirmasi
|
||||
if not messagebox.askyesno("Konfirmasi", f"Terima pembayaran sebesar Rp {int(total):,}?"):
|
||||
return
|
||||
confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?")
|
||||
if confirm:
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Update status jadi 'Paid' dan metode pembayaran (misal Cash default)
|
||||
cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='Cash' WHERE id=?", (transaksi_id,))
|
||||
|
||||
# Update status jadi 'Paid'
|
||||
cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='CASH' WHERE id=?", (trans_id,))
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
|
||||
self.update_data()
|
||||
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
|
||||
self.update_data()
|
||||
@ -2,57 +2,52 @@ 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 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
|
||||
|
||||
tk.Label(self, text="LOGIN SYSTEM", font=("Arial", 20, "bold")).pack(pady=40)
|
||||
# Center Content
|
||||
center_frame = tk.Frame(self)
|
||||
center_frame.place(relx=0.5, rely=0.5, anchor="center")
|
||||
|
||||
frame_login = tk.Frame(self)
|
||||
frame_login.pack()
|
||||
tk.Label(center_frame, text="LOGIN CAFE", font=("Arial", 20, "bold")).pack(pady=20)
|
||||
|
||||
tk.Label(frame_login, text="Username:").pack()
|
||||
self.user = tk.Entry(frame_login)
|
||||
tk.Label(center_frame, text="Username").pack(anchor="w")
|
||||
self.user = tk.Entry(center_frame, width=30)
|
||||
self.user.pack(pady=5)
|
||||
|
||||
tk.Label(frame_login, text="Password:").pack()
|
||||
self.passw = tk.Entry(frame_login, show="*")
|
||||
tk.Label(center_frame, text="Password").pack(anchor="w")
|
||||
self.passw = tk.Entry(center_frame, show="*", width=30)
|
||||
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(center_frame, text="Login", bg="#4CAF50", fg="white", width=20, command=self.login).pack(pady=20)
|
||||
|
||||
def login(self):
|
||||
u = self.user.get()
|
||||
p = self.passw.get()
|
||||
username = self.user.get()
|
||||
password = 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=?", (username, password))
|
||||
res = cur.fetchone()
|
||||
db.close()
|
||||
|
||||
if not res:
|
||||
messagebox.showerror("Gagal", "Username/Password salah!")
|
||||
messagebox.showerror("Error", "Username/Password salah!")
|
||||
return
|
||||
|
||||
role = res[0]
|
||||
|
||||
# Reset input
|
||||
self.user.delete(0, tk.END)
|
||||
self.passw.delete(0, tk.END)
|
||||
|
||||
# Redirect sesuai Role
|
||||
# Arahkan sesuai Role (Nama Class harus sama dengan di main.py)
|
||||
if role == "admin":
|
||||
self.controller.show_frame("AdminPage")
|
||||
elif role == "pembeli":
|
||||
self.controller.show_frame("PembeliPage")
|
||||
self.controller.show_frame("PembeliMenu")
|
||||
elif role == "kasir":
|
||||
self.controller.show_frame("KasirPage")
|
||||
elif role == "pemilik":
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import tkinter as tk
|
||||
from database import setup_database
|
||||
|
||||
# Import class yang sudah kita benerin namanya
|
||||
# Pastikan nama file dan nama class sesuai
|
||||
from login import LoginPage
|
||||
from admin_menu import AdminPage
|
||||
from pembeli_menu import PembeliPage
|
||||
from admin_menu import AdminPage # Class diganti jadi AdminPage di file admin_menu
|
||||
from pembeli_menu import PembeliMenu # Class tetap PembeliMenu
|
||||
from kasir import KasirPage
|
||||
from pemilik import PemilikPage
|
||||
from waiter_dashboard import WaiterPage
|
||||
@ -12,10 +12,10 @@ from waiter_dashboard import WaiterPage
|
||||
class CafeApp(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title("Sistem Manajemen Kafe Python")
|
||||
self.title("Sistem Manajemen Kafe")
|
||||
self.geometry("1000x700")
|
||||
|
||||
setup_database() # Auto bikin tabel
|
||||
setup_database()
|
||||
|
||||
self.container = tk.Frame(self)
|
||||
self.container.pack(side="top", fill="both", expand=True)
|
||||
@ -24,8 +24,8 @@ class CafeApp(tk.Tk):
|
||||
|
||||
self.frames = {}
|
||||
|
||||
# Loop semua halaman
|
||||
for F in (LoginPage, AdminPage, PembeliPage, KasirPage, PemilikPage, WaiterPage):
|
||||
# Loop semua class halaman
|
||||
for F in (LoginPage, AdminPage, PembeliMenu, KasirPage, PemilikPage, WaiterPage):
|
||||
page_name = F.__name__
|
||||
frame = F(parent=self.container, controller=self)
|
||||
self.frames[page_name] = frame
|
||||
|
||||
@ -1,169 +1,177 @@
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox, ttk
|
||||
from tkinter import messagebox, simpledialog
|
||||
from PIL import Image, ImageTk
|
||||
import os
|
||||
from database import connect
|
||||
|
||||
class PembeliPage(tk.Frame): # Ganti nama class jadi PembeliPage
|
||||
class PembeliMenu(tk.Frame):
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
self.cart = {}
|
||||
self.nama_pelanggan = ""
|
||||
self.no_meja = ""
|
||||
self.keranjang = [] # List untuk simpan belanjaan sementara
|
||||
self.image_refs = [] # Supaya gambar tidak hilang
|
||||
|
||||
self.login_frame = tk.Frame(self)
|
||||
self.menu_frame = tk.Frame(self)
|
||||
self.show_login_ui()
|
||||
# --- Layout Utama: Kiri (Menu) & Kanan (Keranjang) ---
|
||||
|
||||
# --- UI 1: LOGIN PEMBELI ---
|
||||
def show_login_ui(self):
|
||||
self.menu_frame.pack_forget()
|
||||
self.login_frame.pack(fill="both", expand=True)
|
||||
# 1. Frame Kiri (Daftar Menu)
|
||||
self.left_frame = tk.Frame(self)
|
||||
self.left_frame.pack(side="left", fill="both", expand=True)
|
||||
|
||||
for w in self.login_frame.winfo_children(): w.destroy()
|
||||
# Header Kiri
|
||||
header = tk.Frame(self.left_frame, bg="#2c3e50", height=50)
|
||||
header.pack(fill="x")
|
||||
tk.Label(header, text="DAFTAR MENU", font=("Arial", 14, "bold"), fg="white", bg="#2c3e50").pack(pady=10)
|
||||
|
||||
tk.Label(self.login_frame, text="SELAMAT DATANG", font=("Arial", 20, "bold")).pack(pady=50)
|
||||
# Canvas untuk Scroll Menu
|
||||
self.canvas = tk.Canvas(self.left_frame)
|
||||
self.scrollbar = tk.Scrollbar(self.left_frame, orient="vertical", command=self.canvas.yview)
|
||||
self.scrollable_frame = tk.Frame(self.canvas)
|
||||
|
||||
box = tk.Frame(self.login_frame, bd=2, relief="groove", padx=20, pady=20)
|
||||
box.pack()
|
||||
self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
|
||||
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
||||
self.canvas.configure(yscrollcommand=self.scrollbar.set)
|
||||
|
||||
tk.Label(box, text="Nama Anda:").pack(anchor="w")
|
||||
self.entry_nama = tk.Entry(box, width=30)
|
||||
self.entry_nama.pack(pady=5)
|
||||
self.canvas.pack(side="left", fill="both", expand=True)
|
||||
self.scrollbar.pack(side="right", fill="y")
|
||||
|
||||
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)
|
||||
# 2. Frame Kanan (Keranjang Belanja)
|
||||
self.right_frame = tk.Frame(self, bg="#ecf0f1", width=300)
|
||||
self.right_frame.pack(side="right", fill="y")
|
||||
self.right_frame.pack_propagate(False) # Agar lebar tetap 300px
|
||||
|
||||
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)
|
||||
tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x")
|
||||
|
||||
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()
|
||||
# Listbox Keranjang
|
||||
self.cart_listbox = tk.Listbox(self.right_frame, font=("Arial", 10))
|
||||
self.cart_listbox.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
# --- UI 2: MENU MAKANAN ---
|
||||
def show_menu_ui(self):
|
||||
self.login_frame.pack_forget()
|
||||
self.menu_frame.pack(fill="both", expand=True)
|
||||
# Label Total Harga
|
||||
self.total_label = tk.Label(self.right_frame, text="Total: Rp 0", font=("Arial", 14, "bold"), bg="#ecf0f1", fg="#e74c3c")
|
||||
self.total_label.pack(pady=10)
|
||||
|
||||
# Bersihkan frame lama biar gak numpuk
|
||||
for w in self.menu_frame.winfo_children(): w.destroy()
|
||||
# Tombol Aksi
|
||||
tk.Button(self.right_frame, text="Hapus Item Terpilih", bg="#e67e22", fg="white", command=self.hapus_item).pack(fill="x", padx=10, pady=5)
|
||||
tk.Button(self.right_frame, text="CHECKOUT / BAYAR", bg="#27ae60", fg="white", font=("Arial", 10, "bold"), height=2, command=self.checkout).pack(fill="x", padx=10, pady=20)
|
||||
tk.Button(self.right_frame, text="Kembali / Logout", command=lambda: controller.show_frame("LoginPage")).pack(pady=5)
|
||||
|
||||
# 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)
|
||||
def update_data(self):
|
||||
"""Dipanggil saat halaman dibuka"""
|
||||
self.load_menu()
|
||||
self.keranjang = [] # Reset keranjang tiap login baru
|
||||
self.update_keranjang_ui()
|
||||
|
||||
content = tk.Frame(self.menu_frame)
|
||||
content.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
# Kiri: Daftar Menu
|
||||
left_frame = tk.LabelFrame(content, text=" Menu Tersedia ")
|
||||
left_frame.pack(side="left", fill="both", expand=True)
|
||||
|
||||
canvas = tk.Canvas(left_frame)
|
||||
scrollbar = tk.Scrollbar(left_frame, orient="vertical", command=canvas.yview)
|
||||
self.scroll_frame = tk.Frame(canvas)
|
||||
|
||||
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)
|
||||
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
|
||||
# Kanan: Keranjang
|
||||
right_frame = tk.LabelFrame(content, text=" Keranjang ")
|
||||
right_frame.pack(side="right", fill="both", width=300, padx=5)
|
||||
|
||||
self.cart_list = tk.Listbox(right_frame)
|
||||
self.cart_list.pack(fill="both", expand=True, padx=5, 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()
|
||||
def load_menu(self):
|
||||
# Bersihkan area menu
|
||||
for widget in self.scrollable_frame.winfo_children():
|
||||
widget.destroy()
|
||||
self.image_refs.clear()
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("SELECT id, nama, harga, stok FROM menu WHERE stok > 0")
|
||||
cur.execute("SELECT * FROM menu")
|
||||
items = 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")
|
||||
columns = 3
|
||||
for index, item in enumerate(items):
|
||||
self.create_card(item, index, columns)
|
||||
|
||||
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)
|
||||
def create_card(self, item, index, columns):
|
||||
id_menu, nama, kategori, harga, stok, file_gambar = item
|
||||
row = index // columns
|
||||
col = index % columns
|
||||
|
||||
col += 1
|
||||
if col > 2:
|
||||
col = 0
|
||||
row += 1
|
||||
card = tk.Frame(self.scrollable_frame, bd=2, relief="groove", bg="white")
|
||||
card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
|
||||
|
||||
def add_to_cart(self, m_id, nama, harga):
|
||||
if m_id in self.cart:
|
||||
self.cart[m_id]['qty'] += 1
|
||||
else:
|
||||
self.cart[m_id] = {'nama': nama, 'harga': harga, 'qty': 1}
|
||||
self.refresh_cart()
|
||||
# Load Gambar
|
||||
path = os.path.join("aset", file_gambar)
|
||||
try:
|
||||
img = Image.open(path).resize((120, 80), Image.Resampling.LANCZOS)
|
||||
photo = ImageTk.PhotoImage(img)
|
||||
self.image_refs.append(photo)
|
||||
tk.Label(card, image=photo, bg="white").pack(pady=5)
|
||||
except:
|
||||
tk.Label(card, text="[No Image]", bg="#eee", height=4).pack(pady=5)
|
||||
|
||||
def refresh_cart(self):
|
||||
self.cart_list.delete(0, tk.END)
|
||||
tk.Label(card, text=nama, font=("Arial", 10, "bold"), bg="white").pack()
|
||||
tk.Label(card, text=f"Rp {int(harga):,}", fg="green", bg="white").pack()
|
||||
|
||||
# Tombol Tambah
|
||||
state = "normal" if stok > 0 else "disabled"
|
||||
text_btn = "Tambah" if stok > 0 else "Habis"
|
||||
tk.Button(card, text=text_btn, bg="#3498db", fg="white", state=state,
|
||||
command=lambda: self.tambah_ke_keranjang(item)).pack(pady=5, padx=5, fill="x")
|
||||
|
||||
def tambah_ke_keranjang(self, item):
|
||||
# item = (id, nama, kategori, harga, stok, gambar)
|
||||
self.keranjang.append(item)
|
||||
self.update_keranjang_ui()
|
||||
|
||||
def update_keranjang_ui(self):
|
||||
self.cart_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.keranjang:
|
||||
nama = item[1]
|
||||
harga = item[3]
|
||||
self.cart_listbox.insert(tk.END, f"{nama} - Rp {int(harga):,}")
|
||||
total += harga
|
||||
|
||||
self.total_label.config(text=f"Total: Rp {int(total):,}")
|
||||
|
||||
def hapus_item(self):
|
||||
selected = self.cart_listbox.curselection()
|
||||
if not selected:
|
||||
return
|
||||
index = selected[0]
|
||||
del self.keranjang[index]
|
||||
self.update_keranjang_ui()
|
||||
|
||||
def checkout(self):
|
||||
if not self.cart: return
|
||||
if not self.keranjang:
|
||||
messagebox.showwarning("Kosong", "Keranjang masih kosong!")
|
||||
return
|
||||
|
||||
total_val = int(self.lbl_total.cget("text").replace("Total: Rp ","").replace(",",""))
|
||||
nama_pelanggan = simpledialog.askstring("Input", "Masukkan Nama Pelanggan:")
|
||||
if not nama_pelanggan: return
|
||||
|
||||
total_harga = sum(item[3] for item in self.keranjang)
|
||||
|
||||
# Simpan ke Database
|
||||
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
|
||||
try:
|
||||
# 1. Simpan Transaksi Utama
|
||||
cur.execute("""
|
||||
INSERT INTO transaksi (nama_pelanggan, total, status)
|
||||
VALUES (?, ?, 'Pending')
|
||||
""", (nama_pelanggan, total_harga))
|
||||
transaksi_id = cur.lastrowid
|
||||
|
||||
# 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))
|
||||
# 2. Simpan Detail Transaksi (Looping item di keranjang)
|
||||
for item in self.keranjang:
|
||||
menu_id = item[0]
|
||||
harga = item[3]
|
||||
# Masukkan ke detail
|
||||
cur.execute("""
|
||||
INSERT INTO detail_transaksi (transaksi_id, menu_id, jumlah, subtotal)
|
||||
VALUES (?, ?, 1, ?)
|
||||
""", (transaksi_id, menu_id, harga))
|
||||
|
||||
db.commit()
|
||||
db.close()
|
||||
# Kurangi Stok Menu
|
||||
cur.execute("UPDATE menu SET stok = stok - 1 WHERE id = ?", (menu_id,))
|
||||
|
||||
messagebox.showinfo("Sukses", "Pesanan terkirim! Mohon tunggu.")
|
||||
self.cart = {}
|
||||
self.refresh_cart()
|
||||
self.load_menu_items()
|
||||
db.commit()
|
||||
messagebox.showinfo("Berhasil", "Pesanan berhasil dibuat! Silakan bayar di kasir.")
|
||||
|
||||
def logout(self):
|
||||
self.cart = {}
|
||||
self.show_login_ui()
|
||||
# Reset
|
||||
self.keranjang = []
|
||||
self.update_keranjang_ui()
|
||||
self.load_menu() # Reload menu untuk update stok visual
|
||||
|
||||
def update_data(self):
|
||||
self.show_login_ui()
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
messagebox.showerror("Error", f"Gagal Checkout: {e}")
|
||||
finally:
|
||||
db.close()
|
||||
@ -1,36 +1,43 @@
|
||||
import tkinter as tk
|
||||
from database import connect
|
||||
|
||||
class PemilikPage(tk.Frame): # HARUS inherit tk.Frame
|
||||
class PemilikPage(tk.Frame):
|
||||
def __init__(self, parent, controller):
|
||||
super().__init__(parent) # Init parent class
|
||||
super().__init__(parent)
|
||||
self.controller = controller
|
||||
|
||||
tk.Label(self, text="LAPORAN PENJUALAN (OWNER)", font=("Arial", 18, "bold")).pack(pady=20)
|
||||
# --- PERBAIKAN: Baris self.pack() sudah DIHAPUS ---
|
||||
# Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid.
|
||||
|
||||
self.box = tk.Frame(self, bd=2, relief="groove", padx=40, pady=40)
|
||||
self.box.pack()
|
||||
# Judul Halaman
|
||||
tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20)
|
||||
|
||||
# Label Total
|
||||
self.total_lbl = tk.Label(self.box, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
|
||||
self.total_lbl.pack(pady=10)
|
||||
self.total_lbl = tk.Label(self, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
|
||||
self.total_lbl.pack(pady=20)
|
||||
|
||||
self.count_lbl = tk.Label(self.box, text="Jumlah Transaksi: 0", font=("Arial", 12))
|
||||
self.count_lbl.pack(pady=5)
|
||||
tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack()
|
||||
|
||||
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)
|
||||
# Tombol-tombol
|
||||
tk.Button(self, text="Refresh Laporan", bg="#cfe2ff", command=self.load).pack(pady=10)
|
||||
tk.Button(self, text="Logout", bg="#f9e79f", command=lambda: controller.show_frame("LoginPage")).pack(pady=10)
|
||||
|
||||
def update_data(self):
|
||||
"""Dipanggil otomatis saat halaman dibuka"""
|
||||
self.load()
|
||||
|
||||
def load(self):
|
||||
"""Ambil data total penjualan dari database"""
|
||||
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()
|
||||
|
||||
# Hitung sum total dari transaksi yang sudah Paid (Lunas)
|
||||
cur.execute("SELECT SUM(total) FROM transaksi WHERE status='Paid'")
|
||||
result = cur.fetchone()[0]
|
||||
|
||||
# Kalau belum ada penjualan, set 0
|
||||
total = result if result else 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 Pendapatan: Rp {int(total_duit):,}")
|
||||
self.count_lbl.config(text=f"Jumlah Transaksi Sukses: {total_transaksi}")
|
||||
# Update tampilan
|
||||
self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}")
|
||||
@ -13,16 +13,16 @@ class WaiterPage(tk.Frame):
|
||||
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)
|
||||
|
||||
# Content
|
||||
tk.Label(self, text="Pesanan Pending (Perlu Diantar):", font=("Arial", 12)).pack(pady=10)
|
||||
# List Pesanan
|
||||
tk.Label(self, text="Pesanan Status: PENDING (Perlu Diantar)", font=("Arial", 12, "bold")).pack(pady=10)
|
||||
|
||||
self.list_orders = tk.Listbox(self, width=80, height=15)
|
||||
self.list_orders = tk.Listbox(self, width=80, height=15, font=("Arial", 10))
|
||||
self.list_orders.pack(pady=5)
|
||||
|
||||
btn_frame = tk.Frame(self)
|
||||
btn_frame.pack(pady=10)
|
||||
|
||||
tk.Button(btn_frame, text="Refresh", command=self.update_data).pack(side="left", padx=5)
|
||||
tk.Button(btn_frame, text="Refresh Data", 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)
|
||||
|
||||
def update_data(self):
|
||||
@ -31,14 +31,14 @@ class WaiterPage(tk.Frame):
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Ambil transaksi yang statusnya 'Pending'
|
||||
# Ambil Transaksi yang statusnya Pending
|
||||
cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Pending'")
|
||||
self.data_orders = cur.fetchall()
|
||||
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]):,}")
|
||||
# item: (id, nama, meja, total)
|
||||
self.list_orders.insert(tk.END, f"ID Transaksi: {item[0]} | Meja: {item[2]} | A.N: {item[1]} | Total: Rp {int(item[3])}")
|
||||
|
||||
def mark_served(self):
|
||||
idx = self.list_orders.curselection()
|
||||
@ -46,14 +46,15 @@ class WaiterPage(tk.Frame):
|
||||
messagebox.showwarning("Pilih", "Pilih pesanan dulu!")
|
||||
return
|
||||
|
||||
trans_id = self.data_orders[idx[0]][0]
|
||||
selected = self.data_orders[idx[0]]
|
||||
transaksi_id = selected[0]
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
# Update status jadi 'Served' biar muncul di Kasir
|
||||
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (trans_id,))
|
||||
# Update Status Transaksi jadi Served
|
||||
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (transaksi_id,))
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
messagebox.showinfo("Sukses", "Pesanan Meja ini sudah dilayani!")
|
||||
messagebox.showinfo("Sukses", "Pesanan Meja selesai dilayani!")
|
||||
self.update_data()
|
||||
Loading…
x
Reference in New Issue
Block a user