This commit is contained in:
Nathan 2025-12-14 22:04:52 +07:00
commit 746a257dd2
22 changed files with 519 additions and 274 deletions

View File

@ -1,40 +1,129 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox, filedialog
from database import connect from database import connect
from tkinter import messagebox import os
import shutil
class AdminMenu: # GANTI NAMA CLASS JADI AdminPage
def __init__(self, root): class AdminPage(tk.Frame):
self.root = root def __init__(self, parent, controller):
self.frame = tk.Frame(root) super().__init__(parent)
self.frame.pack() self.controller = controller
self.selected_image_path = None
self.data_menu = []
tk.Label( # --- Header ---
self.frame, top = tk.Frame(self, bg="#333")
text="ADMIN - KELOLA MENU", top.pack(fill="x")
font=("Arial", 18, "bold") tk.Label(top, text="ADMIN DASHBOARD", font=("Arial", 16, "bold"), fg="white", bg="#333").pack(side="left", padx=10, pady=10)
).pack(pady=10) tk.Button(top, text="Logout", bg="#ff6b6b", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10)
self.nama = tk.Entry(self.frame) # --- Layout Kiri (Form) & Kanan (Tabel) ---
self.harga = tk.Entry(self.frame) main_content = tk.Frame(self)
self.gambar = tk.Entry(self.frame) main_content.pack(fill="both", expand=True, padx=10, pady=10)
for label, entry in [ # --- KIRI ---
("Nama Menu", self.nama), left_frame = tk.Frame(main_content)
("Harga", self.harga), left_frame.pack(side="left", fill="y", padx=10)
("Path Gambar", self.gambar)
]:
tk.Label(self.frame, text=label).pack()
entry.pack()
tk.Button(self.frame, text="Tambah Menu", command=self.tambah).pack(pady=5) 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(left_frame, text="Harga:").pack(anchor="w")
self.entry_harga = tk.Entry(left_frame, width=30)
self.entry_harga.pack(pady=5)
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")
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")
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)
right_frame.pack(side="right", fill="both", expand=True)
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)
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)
def update_data(self):
self.list_menu.delete(0, tk.END)
self.data_menu = []
def tambah(self):
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute( cur.execute("SELECT id, nama, harga, stok FROM menu")
"INSERT INTO menu(nama,harga,gambar) VALUES (?,?,?)", rows = cur.fetchall()
(self.nama.get(), self.harga.get(), self.gambar.get()) 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]})")
def browse_image(self):
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")
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")
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, final_image_path))
db.commit()
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan!")
self.update_data()
except Exception as e:
messagebox.showerror("Error", str(e))
finally:
db.close()
def delete_menu(self):
idx = self.list_menu.curselection()
if not idx: return
selected = self.data_menu[idx[0]]
if messagebox.askyesno("Hapus", f"Hapus {selected[1]}?"):
db = connect()
db.cursor().execute("DELETE FROM menu WHERE id=?", (selected[0],))
db.commit() db.commit()
db.close() db.close()
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan") self.update_data()

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
project/aset/bakso.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
project/aset/es_teh.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
project/aset/jus_jeruk.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
project/aset/mie_ayam.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

View File

@ -1,3 +1,4 @@
# File: database.py
import sqlite3 import sqlite3
def connect(): def connect():
@ -7,7 +8,7 @@ def setup_database():
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Users # 1. Users
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS users( CREATE TABLE IF NOT EXISTS users(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -17,56 +18,57 @@ def setup_database():
) )
""") """)
# Menu # 2. Menu
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS menu( CREATE TABLE IF NOT EXISTS menu(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
nama TEXT, nama TEXT,
kategori TEXT,
harga REAL, harga REAL,
stok INTEGER,
gambar TEXT gambar TEXT
) )
""") """)
# Orders # 3. Transaksi
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS orders( CREATE TABLE IF NOT EXISTS transaksi(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
nama TEXT, nama_pelanggan TEXT,
total REAL meja_id INTEGER,
tanggal TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total REAL,
metode_pembayaran TEXT,
status TEXT DEFAULT 'Pending'
) )
""") """)
# Pembayaran # 4. Detail Transaksi
cur.execute(""" cur.execute("""
CREATE TABLE IF NOT EXISTS pembayaran( CREATE TABLE IF NOT EXISTS detail_transaksi(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER, transaksi_id INTEGER,
total REAL menu_id INTEGER,
jumlah INTEGER,
subtotal REAL
) )
""") """)
# User default # Seeding User (Kalau kosong diisi default)
cur.execute("SELECT COUNT(*) FROM users") cur.execute("SELECT COUNT(*) FROM users")
if cur.fetchone()[0] == 0: if cur.fetchone()[0] == 0:
users = [ users = [
("admin","admin","admin"), ("admin","admin","admin"),
("kasir","kasir","kasir"), ("kasir","kasir","kasir"),
("pembeli","pembeli","pembeli"), ("pembeli","pembeli","pembeli"),
("pemilik","pemilik","pemilik") ("pemilik","pemilik","pemilik"),
("waiter","waiter","waiter")
] ]
cur.executemany("INSERT INTO users(username,password,role) VALUES (?,?,?)", users) cur.executemany("INSERT INTO users(username,password,role) VALUES (?,?,?)", users)
db.commit()
# Menu default
cur.execute("SELECT COUNT(*) FROM menu")
if cur.fetchone()[0] == 0:
menu = [
("Mie Ayam", 18000, "mie ayam.webp"),
("Mie Kuah", 10000, "mie kuah.webp"),
("Es Teh Manis", 5000, "es teh.webp"),
("Jus Jeruk", 8000, "jus jeruk.webp")
]
cur.executemany("INSERT INTO menu(nama,harga,gambar) VALUES (?,?,?)", menu)
db.commit() db.commit()
db.close() db.close()
print("Database Ready!")
if __name__ == "__main__":
setup_database()

32
project/isi_menu.py Normal file
View File

@ -0,0 +1,32 @@
import sqlite3
def isi_data_awal():
conn = sqlite3.connect("cafe.db")
cur = conn.cursor()
# Hapus data lama biar gak dobel (opsional, biar bersih)
cur.execute("DELETE FROM menu")
# Data Menu (Sesuaikan nama file gambar dengan yang ada di folder aset kamu)
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"),
]
print("Sedang mengisi data menu...")
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()

View File

@ -2,73 +2,60 @@ import tkinter as tk
from tkinter import messagebox from tkinter import messagebox
from database import connect from database import connect
class KasirPage: class KasirPage(tk.Frame):
def __init__(self, parent, controller): def __init__(self, parent, controller):
self.parent = parent super().__init__(parent)
self.controller = controller self.controller = controller
self.frame = tk.Frame(parent)
self.frame.pack(fill="both", expand=True)
tk.Label(self.frame, text="KASIR PAGE", font=("Arial", 18, "bold")).pack(pady=10) # 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)
# Tombol Logout # List Order
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5) 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)
# Listbox untuk menampilkan order tk.Button(self, text="Refresh Data", command=self.update_data).pack(pady=5)
tk.Label(self.frame, text="Daftar Order:", font=("Arial", 12, "bold")).pack(pady=5) tk.Button(self, text="💰 PROSES BAYAR (LUNAS)", bg="#81C784", height=2, command=self.bayar).pack(fill="x", padx=50, pady=20)
self.listbox = tk.Listbox(self.frame, width=50, height=10)
self.listbox.pack(pady=5)
# Tombol bayar def update_data(self):
tk.Button(self.frame, text="Bayar", bg="#d1e7dd", command=self.bayar).pack(pady=5) self.order_list.delete(0, tk.END)
self.data_orders = []
self.load_orders() # Load order dari database saat awal
def load_orders(self):
self.listbox.delete(0, tk.END)
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT id, nama, harga FROM orders") # Kasir melihat yang statusnya Served
self.data = cur.fetchall() cur.execute("SELECT id, nama_pelanggan, meja_id, total FROM transaksi WHERE status='Served'")
self.data_orders = cur.fetchall()
db.close() db.close()
for order in self.data: for item in self.data_orders:
self.listbox.insert(tk.END, f"ID {order[0]}: {order[1]} - Rp {order[2]:,}") self.order_list.insert(tk.END, f"ID: {item[0]} | Meja {item[2]} | {item[1]} | Tagihan: Rp {int(item[3])}")
def bayar(self): def bayar(self):
idx = self.listbox.curselection() idx = self.order_list.curselection()
if not idx: if not idx:
messagebox.showwarning("Pilih Order", "Pilih order yang ingin dibayar!") messagebox.showwarning("Pilih", "Pilih tagihan yang mau dibayar!")
return return
order = self.data[idx[0]] selected = self.data_orders[idx[0]]
order_id, nama, total = order transaksi_id = selected[0]
nama = selected[1]
total = selected[3]
# Simpan pembayaran di database (opsional, bisa buat tabel pembayaran) confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?")
if confirm:
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("INSERT INTO pembayaran VALUES (NULL, ?, ?)", (order_id, total))
cur.execute("DELETE FROM orders WHERE id=?", (order_id,)) # Update status jadi 'Paid' dan metode pembayaran (misal Cash default)
cur.execute("UPDATE transaksi SET status='Paid', metode_pembayaran='Cash' WHERE id=?", (transaksi_id,))
db.commit() db.commit()
db.close() db.close()
# Tampilkan struk messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
self.tampil_struk(order_id, nama, total) self.update_data()
# Update listbox
self.load_orders()
def tampil_struk(self, order_id, nama, total):
win = tk.Toplevel(self.frame)
win.title("Struk Pembayaran")
tk.Label(win, text="STRUK PEMBAYARAN", font=("Arial",14,"bold")).pack(pady=5)
tk.Label(win, text=f"Order ID : {order_id}").pack()
tk.Label(win, text=f"Nama Menu: {nama}").pack()
tk.Label(win, text=f"Total : Rp {total:,}").pack()
tk.Label(win, text="Terima kasih 🙏").pack(pady=10)
tk.Button(win, text="Tutup", command=win.destroy).pack(pady=5)
def logout(self):
self.frame.destroy()
self.controller.show_frame("Login")

View File

@ -1,46 +1,56 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox from tkinter import messagebox
from database import connect from database import connect
from admin_menu import AdminMenu
from pembeli_menu import PembeliMenu
from kasir import KasirPage
from pemilik import PemilikPage
class LoginPage: class LoginPage(tk.Frame):
def __init__(self, root): def __init__(self, parent, controller):
self.root = root super().__init__(parent)
self.frame = tk.Frame(root) self.controller = controller
self.frame.pack(expand=True)
tk.Label(self.frame, text="LOGIN CAFE", font=("Arial",20,"bold")).pack(pady=10) # Center Content
center_frame = tk.Frame(self)
center_frame.place(relx=0.5, rely=0.5, anchor="center")
self.user = tk.Entry(self.frame) tk.Label(center_frame, text="LOGIN CAFE", font=("Arial", 20, "bold")).pack(pady=20)
self.passw = tk.Entry(self.frame, show="*")
self.user.pack()
self.passw.pack()
tk.Button(self.frame, text="Login", command=self.login).pack(pady=5) tk.Label(center_frame, text="Username").pack(anchor="w")
self.user = tk.Entry(center_frame, width=30)
self.user.pack(pady=5)
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(center_frame, text="Login", bg="#4CAF50", fg="white", width=20, command=self.login).pack(pady=20)
def login(self): def login(self):
username = self.user.get()
password = self.passw.get()
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT role FROM users WHERE username=? AND password=?", cur.execute("SELECT role FROM users WHERE username=? AND password=?", (username, password))
(self.user.get(), self.passw.get()))
res = cur.fetchone() res = cur.fetchone()
db.close() db.close()
if not res: if not res:
messagebox.showerror("Error","Login gagal") messagebox.showerror("Error", "Username/Password salah!")
return return
self.frame.destroy()
role = res[0] role = res[0]
if role=="admin": # Reset input
AdminMenu(self.root) self.user.delete(0, tk.END)
elif role=="pembeli": self.passw.delete(0, tk.END)
PembeliMenu(self.root)
elif role=="kasir": # Arahkan sesuai Role (Nama Class harus sama dengan di main.py)
KasirPage(self.root) if role == "admin":
elif role=="pemilik": self.controller.show_frame("AdminPage")
PemilikPage(self.root) elif role == "pembeli":
self.controller.show_frame("PembeliMenu")
elif role == "kasir":
self.controller.show_frame("KasirPage")
elif role == "pemilik":
self.controller.show_frame("PemilikPage")
elif role == "waiter":
self.controller.show_frame("WaiterPage")

View File

@ -1,60 +1,44 @@
import tkinter as tk import tkinter as tk
from database import setup_database, connect from database import setup_database
from pembeli_menu import PembeliMenu
# Pastikan nama file dan nama class sesuai
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 kasir import KasirPage from kasir import KasirPage
from pemilik import PemilikPage from pemilik import PemilikPage
from tkinter import messagebox from waiter_dashboard import WaiterPage
# --- Login Screen --- 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="LOGIN SISTEM CAFE", font=("Arial",18,"bold")).pack(pady=20)
tk.Label(self.frame,text="Username").pack()
self.username_entry = tk.Entry(self.frame)
self.username_entry.pack()
tk.Label(self.frame,text="Password").pack()
self.password_entry = tk.Entry(self.frame, show="*")
self.password_entry.pack(pady=5)
tk.Button(self.frame,text="Login", bg="#cfe2ff", command=self.login).pack(pady=10)
def login(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=="pembeli":
PembeliMenu(self.root)
elif role=="kasir":
KasirPage(self.root)
elif role=="pemilik":
PemilikPage(self.root)
else:
messagebox.showerror("Error","Role tidak dikenali")
else:
messagebox.showerror("Error","Username / Password salah")
# --- App ---
class App(tk.Tk):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.title("Sistem Cafe") self.title("Sistem Manajemen Kafe")
self.geometry("700x600") self.geometry("1000x700")
# --- Run Program ---
if __name__=="__main__":
setup_database() setup_database()
app = App()
LoginScreen(app) 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 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
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()
if __name__ == "__main__":
app = CafeApp()
app.mainloop() app.mainloop()

View File

@ -1,91 +1,177 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox, simpledialog
from PIL import Image, ImageTk from PIL import Image, ImageTk
import os
from database import connect from database import connect
from tkinter import messagebox
class PembeliMenu: class PembeliMenu(tk.Frame):
def __init__(self, parent): def __init__(self, parent, controller):
self.parent = parent super().__init__(parent)
self.frame = tk.Frame(parent) self.controller = controller
self.frame.pack(fill="both", expand=True) self.keranjang = [] # List untuk simpan belanjaan sementara
self.image_refs = [] # Supaya gambar tidak hilang
# --- Layout Utama: Kiri (Menu) & Kanan (Keranjang) ---
self.cart = [] # Simpan tuple (nama, harga) # 1. Frame Kiri (Daftar Menu)
self.images = [] # Simpan reference gambar agar tidak dihapus GC self.left_frame = tk.Frame(self)
self.left_frame.pack(side="left", fill="both", expand=True)
# --- Judul Halaman --- # Header Kiri
tk.Label(self.frame, text="MENU PEMBELI", font=("Arial", 18, "bold")).pack(pady=10) 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)
# --- Tombol Logout --- # Canvas untuk Scroll Menu
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5) 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)
# --- Frame Menu --- self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
self.menu_frame = tk.Frame(self.frame) self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.menu_frame.pack(pady=10) self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.load_menu() # Load menu dari database self.canvas.pack(side="left", fill="both", expand=True)
self.scrollbar.pack(side="right", fill="y")
# --- Daftar Pesanan --- # 2. Frame Kanan (Keranjang Belanja)
tk.Label(self.frame, text="Daftar Pesanan:", font=("Arial", 12, "bold")).pack(pady=5) self.right_frame = tk.Frame(self, bg="#ecf0f1", width=300)
self.listbox = tk.Listbox(self.frame, width=50, height=6) self.right_frame.pack(side="right", fill="y")
self.listbox.pack() self.right_frame.pack_propagate(False) # Agar lebar tetap 300px
# --- Total --- tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x")
self.total_lbl = tk.Label(self.frame, text="Total: Rp 0", font=("Arial", 12, "bold"))
self.total_lbl.pack(pady=5)
# --- Tombol Checkout --- # Listbox Keranjang
tk.Button(self.frame, text="Checkout", bg="#d1e7dd", command=self.checkout).pack(pady=5) self.cart_listbox = tk.Listbox(self.right_frame, font=("Arial", 10))
self.cart_listbox.pack(fill="both", expand=True, padx=10, pady=10)
# 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)
# 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)
def update_data(self):
"""Dipanggil saat halaman dibuka"""
self.load_menu()
self.keranjang = [] # Reset keranjang tiap login baru
self.update_keranjang_ui()
def load_menu(self): def load_menu(self):
# Bersihkan area menu
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
self.image_refs.clear()
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT nama, harga, gambar FROM menu") cur.execute("SELECT * FROM menu")
data = cur.fetchall() items = cur.fetchall()
db.close() db.close()
for i, (nama, harga, gambar) in enumerate(data): columns = 3
f = tk.Frame(self.menu_frame, bd=2, relief="ridge") for index, item in enumerate(items):
f.grid(row=i//3, column=i%3, padx=10, pady=10) 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")
card.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
# Load Gambar
path = os.path.join("aset", file_gambar)
try: try:
img = Image.open(gambar).resize((120, 90)) img = Image.open(path).resize((120, 80), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(img) photo = ImageTk.PhotoImage(img)
self.images.append(photo) self.image_refs.append(photo)
tk.Label(f, image=photo).pack() tk.Label(card, image=photo, bg="white").pack(pady=5)
except FileNotFoundError: except:
tk.Label(f, text="No Image").pack() tk.Label(card, text="[No Image]", bg="#eee", height=4).pack(pady=5)
tk.Label(f, text=nama).pack() tk.Label(card, text=nama, font=("Arial", 10, "bold"), bg="white").pack()
tk.Label(f, text=f"Rp {harga:,}").pack() tk.Label(card, text=f"Rp {int(harga):,}", fg="green", bg="white").pack()
tk.Button(f, text="Pesan", bg="#cfe2ff",
command=lambda n=nama, h=harga: self.add(n, h)).pack(pady=3)
def add(self, nama, harga): # Tombol Tambah
self.cart.append((nama, harga)) state = "normal" if stok > 0 else "disabled"
self.listbox.insert(tk.END, f"{nama} - Rp {harga:,}") text_btn = "Tambah" if stok > 0 else "Habis"
total = sum(h for _, h in self.cart) tk.Button(card, text=text_btn, bg="#3498db", fg="white", state=state,
self.total_lbl.config(text=f"Total: Rp {total:,}") 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 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): def checkout(self):
if not self.cart: if not self.keranjang:
messagebox.showwarning("Pesan Kosong", "Belum ada pesanan!") messagebox.showwarning("Kosong", "Keranjang masih kosong!")
return return
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() db = connect()
cur = db.cursor() cur = db.cursor()
for nama, harga in self.cart:
cur.execute("INSERT INTO orders VALUES (NULL, ?, ?)", (nama, harga)) try:
# 1. Simpan Transaksi Utama
cur.execute("""
INSERT INTO transaksi (nama_pelanggan, total, status)
VALUES (?, ?, 'Pending')
""", (nama_pelanggan, total_harga))
transaksi_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,))
db.commit() 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() db.close()
messagebox.showinfo("Sukses", "Pesanan dikirim ke kasir")
self.cart.clear()
self.listbox.delete(0, tk.END)
self.total_lbl.config(text="Total: Rp 0")
def logout(self):
self.frame.destroy()
from main import LoginScreen
LoginScreen(self.parent)

View File

@ -1,38 +1,43 @@
import tkinter as tk import tkinter as tk
from database import connect from database import connect
class PemilikPage: class PemilikPage(tk.Frame):
def __init__(self, parent, controller): def __init__(self, parent, controller):
self.parent = parent super().__init__(parent)
self.controller = controller self.controller = controller
self.frame = tk.Frame(parent)
self.frame.pack(fill="both", expand=True)
tk.Label(self.frame, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=10) # --- PERBAIKAN: Baris self.pack() sudah DIHAPUS ---
# Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid.
# Label untuk total penjualan # Judul Halaman
self.total_lbl = tk.Label(self.frame, text="Total Penjualan: Rp 0", font=("Arial", 14, "bold")) tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20)
self.total_lbl.pack(pady=5)
# Tombol Refresh # Label Total
tk.Button(self.frame, text="Refresh", bg="#cfe2ff", command=self.load).pack(pady=5) self.total_lbl = tk.Label(self, text="Total Pendapatan: Rp 0", font=("Arial", 20, "bold"), fg="green")
self.total_lbl.pack(pady=20)
# Tombol Logout tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack()
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
# Load data awal # 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() self.load()
def load(self): def load(self):
"""Ambil data total penjualan dari database"""
db = connect() db = connect()
cur = db.cursor() cur = db.cursor()
# Jumlahkan semua total pembayaran dari tabel pembayaran
cur.execute("SELECT SUM(total) FROM pembayaran") # Hitung sum total dari transaksi yang sudah Paid (Lunas)
total = cur.fetchone()[0] or 0 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() db.close()
self.total_lbl.config(text=f"Total Penjualan: Rp {total:,}") # Update tampilan
self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}")
def logout(self):
self.frame.destroy()
self.controller.show_frame("Login")

View File

@ -1,10 +1,60 @@
import tkinter as tk import tkinter as tk
from tkinter import messagebox
from database import connect
class WaiterDashboard(tk.Frame): class WaiterPage(tk.Frame):
def __init__(self, parent, controller): def __init__(self, parent, controller):
super().__init__(parent) super().__init__(parent)
self.controller = controller
tk.Label(self, text="WAITER", font=("Arial", 20, "bold")).pack(pady=20) # Header
tk.Label(self, text="(Input pesanan manual)").pack() 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)
tk.Button(self, text="Logout", command=lambda: controller.show_frame("Login")).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, 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 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):
self.list_orders.delete(0, tk.END)
self.data_orders = []
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()
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])}")
def mark_served(self):
idx = self.list_orders.curselection()
if not idx:
messagebox.showwarning("Pilih", "Pilih pesanan dulu!")
return
selected = self.data_orders[idx[0]]
transaksi_id = selected[0]
db = connect()
cur = db.cursor()
# 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 selesai dilayani!")
self.update_data()