Compare commits

...
2 Commits
16 changed files with 278 additions and 274 deletions
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.
+22 -25
View File
@@ -4,27 +4,21 @@ from database import connect
import os
import shutil
# GANTI NAMA CLASS JADI AdminPage
class AdminPage(tk.Frame):
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
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: controller.show_frame("LoginPage")).pack(side="right", padx=10)
tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: self.controller.show_frame("LoginPage")).pack(side="right", padx=10)
# --- 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)
# --- Form Input (Kiri) ---
left_frame = tk.Frame(self, padx=10, pady=10)
left_frame.pack(side="left", fill="y")
tk.Label(left_frame, text="Nama Menu:").pack(anchor="w")
self.entry_nama = tk.Entry(left_frame, width=30)
@@ -36,7 +30,6 @@ class AdminPage(tk.Frame):
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")
@@ -45,16 +38,17 @@ class AdminPage(tk.Frame):
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)
# --- KANAN ---
right_frame = tk.Frame(main_content)
# --- Tabel Menu (Kanan) ---
right_frame = tk.Frame(self, padx=10, pady=10)
right_frame.pack(side="right", fill="both", expand=True)
tk.Label(right_frame, text="Daftar Menu:", font=("Arial", 10, "bold")).pack(anchor="w")
tk.Label(right_frame, text="Daftar Menu Database:", font=("Arial", 10, "bold")).pack(anchor="w")
self.list_menu = tk.Listbox(right_frame)
self.list_menu.pack(fill="both", expand=True)
@@ -79,7 +73,7 @@ class AdminPage(tk.Frame):
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=[("Images", "*.png;*.jpg;*.jpeg")])
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")
@@ -96,20 +90,21 @@ class AdminPage(tk.Frame):
final_image_path = "default.png"
if self.selected_image_path:
if not os.path.exists("img"): os.makedirs("img")
destinasi = os.path.join("img", os.path.basename(self.selected_image_path))
try:
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
except:
pass
db = connect()
cur = db.cursor()
try:
cur.execute("INSERT INTO menu (nama, harga, stok, gambar) VALUES (?, ?, ?, ?)",
(nama, harga, stok, final_image_path))
(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))
@@ -118,12 +113,14 @@ class AdminPage(tk.Frame):
def delete_menu(self):
idx = self.list_menu.curselection()
if not idx: return
if not idx:
messagebox.showwarning("Pilih", "Pilih menu dulu!")
return
selected = self.data_menu[idx[0]]
if messagebox.askyesno("Hapus", f"Hapus {selected[1]}?"):
id_menu = self.data_menu[idx[0]][0]
if messagebox.askyesno("Hapus", "Yakin hapus menu ini?"):
db = connect()
db.cursor().execute("DELETE FROM menu WHERE id=?", (selected[0],))
db.cursor().execute("DELETE FROM menu WHERE id=?", (id_menu,))
db.commit()
db.close()
self.update_data()
+4 -4
View File
@@ -1,4 +1,3 @@
# File: database.py
import sqlite3
def connect():
@@ -30,7 +29,7 @@ def setup_database():
)
""")
# 3. Transaksi
# 3. Transaksi (Header)
cur.execute("""
CREATE TABLE IF NOT EXISTS transaksi(
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -42,6 +41,7 @@ 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 User (Kalau kosong diisi default)
# Seeding Data User Dummy
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!")
print("Database Ready bos!")
if __name__ == "__main__":
setup_database()
+29 -12
View File
@@ -1,32 +1,49 @@
import sqlite3
import os
# PERHATIKAN: Ini harus setup_database, BUKAN init_db
from database import setup_database
def isi_data_awal():
conn = sqlite3.connect("cafe.db")
# 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()
# Hapus data lama biar gak dobel (opsional, biar bersih)
print("♻️ Membersihkan data lama...")
cur.execute("DELETE FROM menu")
cur.execute("DELETE FROM sqlite_sequence WHERE name='menu'") # Reset ID jadi 1
# Data Menu (Sesuaikan nama file gambar dengan yang ada di folder aset kamu)
# 3. Data Menu (Path gambar pakai 'aset/')
menus = [
# (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"),
("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...")
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()
print("✅ Berhasil! Data menu sudah masuk database.")
if __name__ == "__main__":
isi_data_awal()
+14 -13
View File
@@ -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 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))
# 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, 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=50, pady=20)
tk.Button(self, text="💰 PROSES BAYAR & LUNAS", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=20, pady=20)
def update_data(self):
self.order_list.delete(0, tk.END)
@@ -27,32 +27,33 @@ class KasirPage(tk.Frame):
db = connect()
cur = db.cursor()
# Kasir melihat yang statusnya Served
# 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()
db.close()
for item in self.data_orders:
self.order_list.insert(tk.END, f"ID: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3])}")
self.order_list.insert(tk.END, f"ID Transaksi: {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 yang mau dibayar!")
messagebox.showwarning("Pilih", "Pilih tagihan dulu!")
return
selected = self.data_orders[idx[0]]
transaksi_id = selected[0]
nama = selected[1]
trans_id = selected[0]
total = selected[3]
confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?")
if confirm:
# Konfirmasi
if not messagebox.askyesno("Konfirmasi", f"Terima pembayaran sebesar Rp {int(total):,}?"):
return
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()
+21 -16
View File
@@ -2,52 +2,57 @@ 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
# Center Content
center_frame = tk.Frame(self)
center_frame.place(relx=0.5, rely=0.5, anchor="center")
tk.Label(self, text="LOGIN SYSTEM", font=("Arial", 20, "bold")).pack(pady=40)
tk.Label(center_frame, text="LOGIN CAFE", font=("Arial", 20, "bold")).pack(pady=20)
frame_login = tk.Frame(self)
frame_login.pack()
tk.Label(center_frame, text="Username").pack(anchor="w")
self.user = tk.Entry(center_frame, width=30)
tk.Label(frame_login, text="Username:").pack()
self.user = tk.Entry(frame_login)
self.user.pack(pady=5)
tk.Label(center_frame, text="Password").pack(anchor="w")
self.passw = tk.Entry(center_frame, show="*", width=30)
tk.Label(frame_login, text="Password:").pack()
self.passw = tk.Entry(frame_login, show="*")
self.passw.pack(pady=5)
tk.Button(center_frame, text="Login", bg="#4CAF50", fg="white", width=20, command=self.login).pack(pady=20)
tk.Button(frame_login, text="LOGIN", command=self.login, width=20, bg="#2196F3", fg="white").pack(pady=20)
def login(self):
username = self.user.get()
password = self.passw.get()
u = self.user.get()
p = self.passw.get()
db = connect()
cur = db.cursor()
cur.execute("SELECT role FROM users WHERE username=? AND password=?", (username, password))
cur.execute("SELECT role FROM users WHERE username=? AND password=?", (u, p))
res = cur.fetchone()
db.close()
if not res:
messagebox.showerror("Error", "Username/Password salah!")
messagebox.showerror("Gagal", "Username/Password salah!")
return
role = res[0]
# Reset input
self.user.delete(0, tk.END)
self.passw.delete(0, tk.END)
# Arahkan sesuai Role (Nama Class harus sama dengan di main.py)
# Redirect sesuai Role
if role == "admin":
self.controller.show_frame("AdminPage")
elif role == "pembeli":
self.controller.show_frame("PembeliMenu")
self.controller.show_frame("PembeliPage")
elif role == "kasir":
self.controller.show_frame("KasirPage")
elif role == "pemilik":
+7 -7
View File
@@ -1,10 +1,10 @@
import tkinter as tk
from database import setup_database
# Pastikan nama file dan nama class sesuai
# Import class yang sudah kita benerin namanya
from login import LoginPage
from admin_menu import AdminPage # Class diganti jadi AdminPage di file admin_menu
from pembeli_menu import PembeliMenu # Class tetap PembeliMenu
from admin_menu import AdminPage
from pembeli_menu import PembeliPage
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")
self.title("Sistem Manajemen Kafe Python")
self.geometry("1000x700")
setup_database()
setup_database() # Auto bikin tabel
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 class halaman
for F in (LoginPage, AdminPage, PembeliMenu, KasirPage, PemilikPage, WaiterPage):
# 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
+127 -135
View File
@@ -1,177 +1,169 @@
import tkinter as tk
from tkinter import messagebox, simpledialog
from PIL import Image, ImageTk
import os
from tkinter import messagebox, ttk
from database import connect
class PembeliMenu(tk.Frame):
class PembeliPage(tk.Frame): # Ganti nama class jadi PembeliPage
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.keranjang = [] # List untuk simpan belanjaan sementara
self.image_refs = [] # Supaya gambar tidak hilang
self.cart = {}
self.nama_pelanggan = ""
self.no_meja = ""
# --- Layout Utama: Kiri (Menu) & Kanan (Keranjang) ---
self.login_frame = tk.Frame(self)
self.menu_frame = tk.Frame(self)
self.show_login_ui()
# 1. Frame Kiri (Daftar Menu)
self.left_frame = tk.Frame(self)
self.left_frame.pack(side="left", fill="both", expand=True)
# --- UI 1: LOGIN PEMBELI ---
def show_login_ui(self):
self.menu_frame.pack_forget()
self.login_frame.pack(fill="both", expand=True)
# 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)
for w in self.login_frame.winfo_children(): w.destroy()
# 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)
tk.Label(self.login_frame, text="SELAMAT DATANG", font=("Arial", 20, "bold")).pack(pady=50)
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)
box = tk.Frame(self.login_frame, bd=2, relief="groove", padx=20, pady=20)
box.pack()
self.canvas.pack(side="left", fill="both", expand=True)
self.scrollbar.pack(side="right", fill="y")
tk.Label(box, text="Nama Anda:").pack(anchor="w")
self.entry_nama = tk.Entry(box, width=30)
self.entry_nama.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.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.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x")
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)
# 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)
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()
# 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)
# --- UI 2: MENU MAKANAN ---
def show_menu_ui(self):
self.login_frame.pack_forget()
self.menu_frame.pack(fill="both", expand=True)
# 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)
# Bersihkan frame lama biar gak numpuk
for w in self.menu_frame.winfo_children(): w.destroy()
def update_data(self):
"""Dipanggil saat halaman dibuka"""
self.load_menu()
self.keranjang = [] # Reset keranjang tiap login baru
self.update_keranjang_ui()
# 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 load_menu(self):
# Bersihkan area menu
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
self.image_refs.clear()
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()
db = connect()
cur = db.cursor()
cur.execute("SELECT * FROM menu")
cur.execute("SELECT id, nama, harga, stok FROM menu WHERE stok > 0")
items = cur.fetchall()
db.close()
columns = 3
for index, item in enumerate(items):
self.create_card(item, index, columns)
def create_card(self, item, index, columns):
id_menu, nama, kategori, harga, stok, file_gambar = item
row = index // columns
col = index % columns
card = tk.Frame(self.scrollable_frame, bd=2, relief="groove", bg="white")
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")
# 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)
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)
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()
col += 1
if col > 2:
col = 0
row += 1
# 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 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()
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)
def refresh_cart(self):
self.cart_list.delete(0, tk.END)
total = 0
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()
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):,}")
def checkout(self):
if not self.keranjang:
messagebox.showwarning("Kosong", "Keranjang masih kosong!")
return
if not self.cart: return
nama_pelanggan = simpledialog.askstring("Input", "Masukkan Nama Pelanggan:")
if not nama_pelanggan: return
total_val = int(self.lbl_total.cget("text").replace("Total: Rp ","").replace(",",""))
total_harga = sum(item[3] for item in self.keranjang)
# Simpan ke Database
db = connect()
cur = db.cursor()
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 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
# 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))
# Kurangi Stok Menu
cur.execute("UPDATE menu SET stok = stok - 1 WHERE id = ?", (menu_id,))
# 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))
db.commit()
messagebox.showinfo("Berhasil", "Pesanan berhasil dibuat! Silakan bayar di kasir.")
# Reset
self.keranjang = []
self.update_keranjang_ui()
self.load_menu() # Reload menu untuk update stok visual
except Exception as e:
db.rollback()
messagebox.showerror("Error", f"Gagal Checkout: {e}")
finally:
db.close()
messagebox.showinfo("Sukses", "Pesanan terkirim! Mohon tunggu.")
self.cart = {}
self.refresh_cart()
self.load_menu_items()
def logout(self):
self.cart = {}
self.show_login_ui()
def update_data(self):
self.show_login_ui()
+19 -26
View File
@@ -1,43 +1,36 @@
import tkinter as tk
from database import connect
class PemilikPage(tk.Frame):
class PemilikPage(tk.Frame): # HARUS inherit tk.Frame
def __init__(self, parent, controller):
super().__init__(parent)
super().__init__(parent) # Init parent class
self.controller = controller
# --- PERBAIKAN: Baris self.pack() sudah DIHAPUS ---
# Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid.
tk.Label(self, text="LAPORAN PENJUALAN (OWNER)", font=("Arial", 18, "bold")).pack(pady=20)
# Judul Halaman
tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20)
self.box = tk.Frame(self, bd=2, relief="groove", padx=40, pady=40)
self.box.pack()
# Label Total
self.total_lbl = tk.Label(self, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
self.total_lbl.pack(pady=20)
self.total_lbl = tk.Label(self.box, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
self.total_lbl.pack(pady=10)
tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack()
self.count_lbl = tk.Label(self.box, text="Jumlah Transaksi: 0", font=("Arial", 12))
self.count_lbl.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)
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)
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 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
# Hitung total dari transaksi yang statusnya 'Paid'
cur.execute("SELECT SUM(total), COUNT(id) FROM transaksi WHERE status='Paid'")
res = cur.fetchone()
db.close()
# Update tampilan
self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}")
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}")
+11 -12
View File
@@ -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)
# List Pesanan
tk.Label(self, text="Pesanan Status: PENDING (Perlu Diantar)", font=("Arial", 12, "bold")).pack(pady=10)
# Content
tk.Label(self, text="Pesanan Pending (Perlu Diantar):", font=("Arial", 12)).pack(pady=10)
self.list_orders = tk.Listbox(self, width=80, height=15, font=("Arial", 10))
self.list_orders = tk.Listbox(self, width=80, height=15)
self.list_orders.pack(pady=5)
btn_frame = tk.Frame(self)
btn_frame.pack(pady=10)
tk.Button(btn_frame, text="Refresh Data", command=self.update_data).pack(side="left", 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)
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 Transaksi: {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: {item[0]} | Meja: {item[2]} | A.N: {item[1]} | Total: Rp {int(item[3]):,}")
def mark_served(self):
idx = self.list_orders.curselection()
@@ -46,15 +46,14 @@ class WaiterPage(tk.Frame):
messagebox.showwarning("Pilih", "Pilih pesanan dulu!")
return
selected = self.data_orders[idx[0]]
transaksi_id = selected[0]
trans_id = self.data_orders[idx[0]][0]
db = connect()
cur = db.cursor()
# Update Status Transaksi jadi Served
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (transaksi_id,))
# Update status jadi 'Served' biar muncul di Kasir
cur.execute("UPDATE transaksi SET status='Served' WHERE id=?", (trans_id,))
db.commit()
db.close()
messagebox.showinfo("Sukses", "Pesanan Meja selesai dilayani!")
messagebox.showinfo("Sukses", "Pesanan Meja ini sudah dilayani!")
self.update_data()