Compare commits

..
2 Commits
22 changed files with 519 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.
+117 -28
View File
@@ -1,40 +1,129 @@
import tkinter as tk
from tkinter import messagebox, filedialog
from database import connect
from tkinter import messagebox
import os
import shutil
class AdminMenu:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(root)
self.frame.pack()
# 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 = []
tk.Label(
self.frame,
text="ADMIN - KELOLA MENU",
font=("Arial", 18, "bold")
).pack(pady=10)
# --- 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)
self.nama = tk.Entry(self.frame)
self.harga = tk.Entry(self.frame)
self.gambar = tk.Entry(self.frame)
# --- Layout Kiri (Form) & Kanan (Tabel) ---
main_content = tk.Frame(self)
main_content.pack(fill="both", expand=True, padx=10, pady=10)
for label, entry in [
("Nama Menu", self.nama),
("Harga", self.harga),
("Path Gambar", self.gambar)
]:
tk.Label(self.frame, text=label).pack()
entry.pack()
# --- KIRI ---
left_frame = tk.Frame(main_content)
left_frame.pack(side="left", fill="y", padx=10)
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()
cur = db.cursor()
cur.execute(
"INSERT INTO menu(nama,harga,gambar) VALUES (?,?,?)",
(self.nama.get(), self.harga.get(), self.gambar.get())
)
cur.execute("SELECT id, nama, harga, stok FROM menu")
rows = cur.fetchall()
db.close()
for row in rows:
self.data_menu.append(row)
self.list_menu.insert(tk.END, f"{row[1]} - Rp {int(row[2])} (Stok: {row[3]})")
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.close()
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan")
self.update_data()
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
View File
Binary file not shown.
+26 -24
View File
@@ -1,3 +1,4 @@
# File: database.py
import sqlite3
def connect():
@@ -7,7 +8,7 @@ def setup_database():
db = connect()
cur = db.cursor()
# Users
# 1. Users
cur.execute("""
CREATE TABLE IF NOT EXISTS users(
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -17,56 +18,57 @@ def setup_database():
)
""")
# Menu
# 2. Menu
cur.execute("""
CREATE TABLE IF NOT EXISTS menu(
id INTEGER PRIMARY KEY AUTOINCREMENT,
nama TEXT,
kategori TEXT,
harga REAL,
stok INTEGER,
gambar TEXT
)
""")
# Orders
# 3. Transaksi
cur.execute("""
CREATE TABLE IF NOT EXISTS orders(
CREATE TABLE IF NOT EXISTS transaksi(
id INTEGER PRIMARY KEY AUTOINCREMENT,
nama TEXT,
total REAL
nama_pelanggan TEXT,
meja_id INTEGER,
tanggal TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total REAL,
metode_pembayaran TEXT,
status TEXT DEFAULT 'Pending'
)
""")
# Pembayaran
# 4. Detail Transaksi
cur.execute("""
CREATE TABLE IF NOT EXISTS pembayaran(
CREATE TABLE IF NOT EXISTS detail_transaksi(
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER,
total REAL
transaksi_id INTEGER,
menu_id INTEGER,
jumlah INTEGER,
subtotal REAL
)
""")
# User default
# Seeding User (Kalau kosong diisi default)
cur.execute("SELECT COUNT(*) FROM users")
if cur.fetchone()[0] == 0:
users = [
("admin","admin","admin"),
("kasir","kasir","kasir"),
("pembeli","pembeli","pembeli"),
("pemilik","pemilik","pemilik")
("pemilik","pemilik","pemilik"),
("waiter","waiter","waiter")
]
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.close()
print("Database Ready!")
if __name__ == "__main__":
setup_database()
+32
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()
+35 -48
View File
@@ -2,73 +2,60 @@ import tkinter as tk
from tkinter import messagebox
from database import connect
class KasirPage:
class KasirPage(tk.Frame):
def __init__(self, parent, controller):
self.parent = parent
super().__init__(parent)
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
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
# 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)
# Listbox untuk menampilkan order
tk.Label(self.frame, text="Daftar Order:", font=("Arial", 12, "bold")).pack(pady=5)
self.listbox = tk.Listbox(self.frame, width=50, height=10)
self.listbox.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)
# Tombol bayar
tk.Button(self.frame, text="Bayar", bg="#d1e7dd", command=self.bayar).pack(pady=5)
def update_data(self):
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()
cur = db.cursor()
cur.execute("SELECT id, nama, harga FROM orders")
self.data = cur.fetchall()
# 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 order in self.data:
self.listbox.insert(tk.END, f"ID {order[0]}: {order[1]} - Rp {order[2]:,}")
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])}")
def bayar(self):
idx = self.listbox.curselection()
idx = self.order_list.curselection()
if not idx:
messagebox.showwarning("Pilih Order", "Pilih order yang ingin dibayar!")
messagebox.showwarning("Pilih", "Pilih tagihan yang mau dibayar!")
return
order = self.data[idx[0]]
order_id, nama, total = order
selected = self.data_orders[idx[0]]
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()
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.close()
# Tampilkan struk
self.tampil_struk(order_id, nama, total)
# 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")
messagebox.showinfo("Lunas", "Transaksi Selesai & Lunas!")
self.update_data()
+33 -23
View File
@@ -1,46 +1,56 @@
import tkinter as tk
from tkinter import messagebox
from database import connect
from admin_menu import AdminMenu
from pembeli_menu import PembeliMenu
from kasir import KasirPage
from pemilik import PemilikPage
class LoginPage:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(root)
self.frame.pack(expand=True)
class LoginPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
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)
self.passw = tk.Entry(self.frame, show="*")
self.user.pack()
self.passw.pack()
tk.Label(center_frame, text="LOGIN CAFE", font=("Arial", 20, "bold")).pack(pady=20)
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):
username = self.user.get()
password = self.passw.get()
db = connect()
cur = db.cursor()
cur.execute("SELECT role FROM users WHERE username=? AND password=?",
(self.user.get(), self.passw.get()))
cur.execute("SELECT role FROM users WHERE username=? AND password=?", (username, password))
res = cur.fetchone()
db.close()
if not res:
messagebox.showerror("Error","Login gagal")
messagebox.showerror("Error", "Username/Password salah!")
return
self.frame.destroy()
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)
if role == "admin":
AdminMenu(self.root)
self.controller.show_frame("AdminPage")
elif role == "pembeli":
PembeliMenu(self.root)
self.controller.show_frame("PembeliMenu")
elif role == "kasir":
KasirPage(self.root)
self.controller.show_frame("KasirPage")
elif role == "pemilik":
PemilikPage(self.root)
self.controller.show_frame("PemilikPage")
elif role == "waiter":
self.controller.show_frame("WaiterPage")
+35 -51
View File
@@ -1,60 +1,44 @@
import tkinter as tk
from database import setup_database, connect
from pembeli_menu import PembeliMenu
from database import setup_database
# 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 pemilik import PemilikPage
from tkinter import messagebox
from waiter_dashboard import WaiterPage
# --- Login Screen ---
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):
class CafeApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("Sistem Cafe")
self.geometry("700x600")
self.title("Sistem Manajemen Kafe")
self.geometry("1000x700")
# --- Run Program ---
if __name__=="__main__":
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()
+145 -59
View File
@@ -1,91 +1,177 @@
import tkinter as tk
from tkinter import messagebox, simpledialog
from PIL import Image, ImageTk
import os
from database import connect
from tkinter import messagebox
class PembeliMenu:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(parent)
self.frame.pack(fill="both", expand=True)
class PembeliMenu(tk.Frame):
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
# --- Layout Utama: Kiri (Menu) & Kanan (Keranjang) ---
self.cart = [] # Simpan tuple (nama, harga)
self.images = [] # Simpan reference gambar agar tidak dihapus GC
# 1. Frame Kiri (Daftar Menu)
self.left_frame = tk.Frame(self)
self.left_frame.pack(side="left", fill="both", expand=True)
# --- Judul Halaman ---
tk.Label(self.frame, text="MENU PEMBELI", font=("Arial", 18, "bold")).pack(pady=10)
# 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)
# --- Tombol Logout ---
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
# 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)
# --- Frame Menu ---
self.menu_frame = tk.Frame(self.frame)
self.menu_frame.pack(pady=10)
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)
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 ---
tk.Label(self.frame, text="Daftar Pesanan:", font=("Arial", 12, "bold")).pack(pady=5)
self.listbox = tk.Listbox(self.frame, width=50, height=6)
self.listbox.pack()
# 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
# --- Total ---
self.total_lbl = tk.Label(self.frame, text="Total: Rp 0", font=("Arial", 12, "bold"))
self.total_lbl.pack(pady=5)
tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x")
# --- Tombol Checkout ---
tk.Button(self.frame, text="Checkout", bg="#d1e7dd", command=self.checkout).pack(pady=5)
# 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)
# 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):
# 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 nama, harga, gambar FROM menu")
data = cur.fetchall()
cur.execute("SELECT * FROM menu")
items = cur.fetchall()
db.close()
for i, (nama, harga, gambar) in enumerate(data):
f = tk.Frame(self.menu_frame, bd=2, relief="ridge")
f.grid(row=i//3, column=i%3, padx=10, pady=10)
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")
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(gambar).resize((120, 90))
img = Image.open(path).resize((120, 80), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(img)
self.images.append(photo)
tk.Label(f, image=photo).pack()
except FileNotFoundError:
tk.Label(f, text="No Image").pack()
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(f, text=nama).pack()
tk.Label(f, text=f"Rp {harga:,}").pack()
tk.Button(f, text="Pesan", bg="#cfe2ff",
command=lambda n=nama, h=harga: self.add(n, h)).pack(pady=3)
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()
def add(self, nama, harga):
self.cart.append((nama, harga))
self.listbox.insert(tk.END, f"{nama} - Rp {harga:,}")
total = sum(h for _, h in self.cart)
self.total_lbl.config(text=f"Total: Rp {total:,}")
# 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 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:
messagebox.showwarning("Pesan Kosong", "Belum ada pesanan!")
if not self.keranjang:
messagebox.showwarning("Kosong", "Keranjang masih kosong!")
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()
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()
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 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)
+26 -21
View File
@@ -1,38 +1,43 @@
import tkinter as tk
from database import connect
class PemilikPage:
class PemilikPage(tk.Frame):
def __init__(self, parent, controller):
self.parent = parent
super().__init__(parent)
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
self.total_lbl = tk.Label(self.frame, text="Total Penjualan: Rp 0", font=("Arial", 14, "bold"))
self.total_lbl.pack(pady=5)
# Judul Halaman
tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20)
# Tombol Refresh
tk.Button(self.frame, text="Refresh", bg="#cfe2ff", command=self.load).pack(pady=5)
# 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)
# Tombol Logout
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack()
# 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()
def load(self):
"""Ambil data total penjualan dari database"""
db = connect()
cur = db.cursor()
# Jumlahkan semua total pembayaran dari tabel pembayaran
cur.execute("SELECT SUM(total) FROM pembayaran")
total = cur.fetchone()[0] or 0
# 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()
self.total_lbl.config(text=f"Total Penjualan: Rp {total:,}")
def logout(self):
self.frame.destroy()
self.controller.show_frame("Login")
# Update tampilan
self.total_lbl.config(text=f"Total Pendapatan: Rp {int(total):,}")
+54 -4
View File
@@ -1,10 +1,60 @@
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):
super().__init__(parent)
self.controller = controller
tk.Label(self, text="WAITER", font=("Arial", 20, "bold")).pack(pady=20)
tk.Label(self, text="(Input pesanan manual)").pack()
# Header
top = tk.Frame(self, bg="#FF9800")
top.pack(fill="x")
tk.Label(top, text="WAITER DASHBOARD", font=("Arial", 16, "bold"), bg="#FF9800", fg="white").pack(side="left", padx=10, pady=10)
tk.Button(top, text="Logout", command=lambda: controller.show_frame("LoginPage")).pack(side="right", padx=10)
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()