Merge branch 'main' of https://git-eng.ukwms.ac.id/5803025001/Python-Menu
This commit is contained in:
commit
746a257dd2
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.
@ -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)
|
||||
|
||||
def tambah(self):
|
||||
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 = []
|
||||
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute(
|
||||
"INSERT INTO menu(nama,harga,gambar) VALUES (?,?,?)",
|
||||
(self.nama.get(), self.harga.get(), self.gambar.get())
|
||||
)
|
||||
db.commit()
|
||||
cur.execute("SELECT id, nama, harga, stok FROM menu")
|
||||
rows = cur.fetchall()
|
||||
db.close()
|
||||
messagebox.showinfo("Sukses", "Menu berhasil ditambahkan")
|
||||
|
||||
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()
|
||||
self.update_data()
|
||||
BIN
project/aset/ayam_goreng.jpg
Normal file
BIN
project/aset/ayam_goreng.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
BIN
project/aset/bakso.jpg
Normal file
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
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
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
BIN
project/aset/mie_ayam.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
project/cafe.db
BIN
project/cafe.db
Binary file not shown.
@ -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.commit()
|
||||
db.close()
|
||||
print("Database Ready!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_database()
|
||||
32
project/isi_menu.py
Normal file
32
project/isi_menu.py
Normal 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()
|
||||
@ -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)
|
||||
|
||||
self.load_orders() # Load order dari database saat awal
|
||||
|
||||
def load_orders(self):
|
||||
self.listbox.delete(0, tk.END)
|
||||
def update_data(self):
|
||||
self.order_list.delete(0, tk.END)
|
||||
self.data_orders = []
|
||||
|
||||
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
|
||||
|
||||
selected = self.data_orders[idx[0]]
|
||||
transaksi_id = selected[0]
|
||||
nama = selected[1]
|
||||
total = selected[3]
|
||||
|
||||
order = self.data[idx[0]]
|
||||
order_id, nama, total = order
|
||||
confirm = messagebox.askyesno("Konfirmasi", f"Terima pembayaran Rp {int(total)} dari {nama}?")
|
||||
if confirm:
|
||||
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,))
|
||||
|
||||
db.commit()
|
||||
db.close()
|
||||
|
||||
# Simpan pembayaran di database (opsional, bisa buat tabel pembayaran)
|
||||
db = connect()
|
||||
cur = db.cursor()
|
||||
cur.execute("INSERT INTO pembayaran VALUES (NULL, ?, ?)", (order_id, total))
|
||||
cur.execute("DELETE FROM orders WHERE id=?", (order_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()
|
||||
@ -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
|
||||
|
||||
# Center Content
|
||||
center_frame = tk.Frame(self)
|
||||
center_frame.place(relx=0.5, rely=0.5, anchor="center")
|
||||
|
||||
tk.Label(self.frame, text="LOGIN CAFE", font=("Arial",20,"bold")).pack(pady=10)
|
||||
tk.Label(center_frame, text="LOGIN CAFE", font=("Arial", 20, "bold")).pack(pady=20)
|
||||
|
||||
self.user = tk.Entry(self.frame)
|
||||
self.passw = tk.Entry(self.frame, show="*")
|
||||
self.user.pack()
|
||||
self.passw.pack()
|
||||
tk.Label(center_frame, text="Username").pack(anchor="w")
|
||||
self.user = tk.Entry(center_frame, width=30)
|
||||
self.user.pack(pady=5)
|
||||
|
||||
tk.Button(self.frame, text="Login", command=self.login).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)
|
||||
|
||||
if role=="admin":
|
||||
AdminMenu(self.root)
|
||||
elif role=="pembeli":
|
||||
PembeliMenu(self.root)
|
||||
elif role=="kasir":
|
||||
KasirPage(self.root)
|
||||
elif role=="pemilik":
|
||||
PemilikPage(self.root)
|
||||
# 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("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")
|
||||
@ -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)
|
||||
app.mainloop()
|
||||
setup_database()
|
||||
|
||||
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()
|
||||
@ -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) ---
|
||||
|
||||
# 1. Frame Kiri (Daftar Menu)
|
||||
self.left_frame = tk.Frame(self)
|
||||
self.left_frame.pack(side="left", fill="both", expand=True)
|
||||
|
||||
self.cart = [] # Simpan tuple (nama, harga)
|
||||
self.images = [] # Simpan reference gambar agar tidak dihapus GC
|
||||
# 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)
|
||||
|
||||
# --- Judul Halaman ---
|
||||
tk.Label(self.frame, text="MENU PEMBELI", font=("Arial", 18, "bold")).pack(pady=10)
|
||||
# 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)
|
||||
|
||||
# --- Tombol Logout ---
|
||||
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
|
||||
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)
|
||||
|
||||
# --- Frame Menu ---
|
||||
self.menu_frame = tk.Frame(self.frame)
|
||||
self.menu_frame.pack(pady=10)
|
||||
self.canvas.pack(side="left", fill="both", expand=True)
|
||||
self.scrollbar.pack(side="right", fill="y")
|
||||
|
||||
self.load_menu() # Load menu dari database
|
||||
# 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
|
||||
|
||||
# --- 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()
|
||||
tk.Label(self.right_frame, text="KERANJANG SAYA", font=("Arial", 12, "bold"), bg="#bdc3c7", pady=10).pack(fill="x")
|
||||
|
||||
# --- Total ---
|
||||
self.total_lbl = tk.Label(self.frame, text="Total: Rp 0", font=("Arial", 12, "bold"))
|
||||
self.total_lbl.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)
|
||||
|
||||
# --- Tombol Checkout ---
|
||||
tk.Button(self.frame, text="Checkout", bg="#d1e7dd", command=self.checkout).pack(pady=5)
|
||||
# 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)
|
||||
|
||||
try:
|
||||
img = Image.open(gambar).resize((120, 90))
|
||||
photo = ImageTk.PhotoImage(img)
|
||||
self.images.append(photo)
|
||||
tk.Label(f, image=photo).pack()
|
||||
except FileNotFoundError:
|
||||
tk.Label(f, text="No Image").pack()
|
||||
def create_card(self, item, index, columns):
|
||||
id_menu, nama, kategori, harga, stok, file_gambar = item
|
||||
row = index // columns
|
||||
col = index % columns
|
||||
|
||||
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)
|
||||
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(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:,}")
|
||||
# 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"), 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 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))
|
||||
db.commit()
|
||||
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")
|
||||
try:
|
||||
# 1. Simpan Transaksi Utama
|
||||
cur.execute("""
|
||||
INSERT INTO transaksi (nama_pelanggan, total, status)
|
||||
VALUES (?, ?, 'Pending')
|
||||
""", (nama_pelanggan, total_harga))
|
||||
transaksi_id = cur.lastrowid
|
||||
|
||||
def logout(self):
|
||||
self.frame.destroy()
|
||||
from main import LoginScreen
|
||||
LoginScreen(self.parent)
|
||||
# 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()
|
||||
@ -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)
|
||||
|
||||
# --- PERBAIKAN: Baris self.pack() sudah DIHAPUS ---
|
||||
# Kita tidak boleh packing diri sendiri karena main.py sudah mengatur posisi kita pakai Grid.
|
||||
|
||||
tk.Label(self.frame, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=10)
|
||||
# Judul Halaman
|
||||
tk.Label(self, text="LAPORAN PENJUALAN", font=("Arial", 18, "bold")).pack(pady=20)
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
tk.Label(self, text="(Menghitung semua transaksi berstatus 'Paid')", fg="gray").pack()
|
||||
|
||||
# Tombol Refresh
|
||||
tk.Button(self.frame, text="Refresh", bg="#cfe2ff", command=self.load).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)
|
||||
|
||||
# Tombol Logout
|
||||
tk.Button(self.frame, text="Logout", bg="#f9e79f", command=self.logout).pack(pady=5)
|
||||
|
||||
# Load data awal
|
||||
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):,}")
|
||||
@ -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
|
||||
|
||||
# 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.Label(self, text="WAITER", font=("Arial", 20, "bold")).pack(pady=20)
|
||||
tk.Label(self, text="(Input pesanan manual)").pack()
|
||||
# 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)
|
||||
|
||||
tk.Button(self, text="Logout", command=lambda: controller.show_frame("Login")).pack(pady=10)
|
||||
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()
|
||||
Loading…
x
Reference in New Issue
Block a user