diff --git a/detail_transaksi.csv b/detail_transaksi.csv index 7fd427f..c5fc900 100644 --- a/detail_transaksi.csv +++ b/detail_transaksi.csv @@ -18,3 +18,8 @@ id,transaksi_id,menu_id,qty,harga_satuan,subtotal_item 17,10,7,1,21000.0,21000.0 18,11,3,1,30000.0,30000.0 19,11,4,1,25000.0,25000.0 +20,12,7,1,21000.0,21000.0 +21,13,8,1,25000.0,25000.0 +22,13,2,1,25000.0,25000.0 +23,13,3,1,30000.0,30000.0 +24,14,10,1,20000.0,20000.0 diff --git a/favorite.csv b/favorite.csv index 409e6e4..1fae188 100644 --- a/favorite.csv +++ b/favorite.csv @@ -3,10 +3,11 @@ user_id,menu_id,order_count,last_ordered 1,2,1,2025-12-13 20:12:35 1,4,1,2025-12-13 20:12:35 1,6,1,2025-12-13 20:12:35 -4,2,2,2025-12-14 10:03:04 +4,2,3,2025-12-14 21:14:46 4,4,3,2025-12-14 10:35:35 -4,3,3,2025-12-14 10:35:35 +4,3,4,2025-12-14 21:14:46 4,11,1,2025-12-14 01:44:46 -4,7,2,2025-12-14 10:03:04 -4,8,1,2025-12-14 09:50:02 +4,7,3,2025-12-14 21:12:55 +4,8,2,2025-12-14 21:14:46 4,9,1,2025-12-14 09:50:02 +4,10,1,2025-12-14 21:43:51 diff --git a/main.py b/main.py index 7675db0..6c7b917 100644 --- a/main.py +++ b/main.py @@ -2827,9 +2827,8 @@ class App: def build_waiter_tab(self, parent, readonly=False): """Tab untuk waiter mengelola pesanan - Args: - parent: Parent frame - readonly: Jika True, tombol aksi disabled (untuk admin monitoring) + Status Flow yang Disederhanakan: + pending → menunggu → selesai """ for w in parent.winfo_children(): w.destroy() @@ -2838,7 +2837,6 @@ class App: header = ttk.Frame(parent) header.pack(fill='x', padx=10, pady=6) - # ✅ UBAH JUDUL SESUAI MODE if readonly: ttk.Label( header, @@ -2849,7 +2847,7 @@ class App: else: ttk.Label( header, - text="Dashboard Waiter - Kelola Pesanan", + text="🍽️ Dashboard Waiter - Kelola Pesanan", font=("Arial", 13, "bold") ).pack(side='left') @@ -2865,8 +2863,7 @@ class App: ttk.Label(filter_frame, text="Filter Status:").pack(side='left', padx=6) ttk.Button(filter_frame, text="Semua", command=lambda: self.reload_waiter_orders(None), width=8).pack(side='left', padx=2) ttk.Button(filter_frame, text="Pending", command=lambda: self.reload_waiter_orders('pending'), width=8).pack(side='left', padx=2) - ttk.Button(filter_frame, text="Menunggu", command=lambda: self.reload_waiter_orders('menunggu'), width=10).pack(side='left', padx=2) - ttk.Button(filter_frame, text="Diproses", command=lambda: self.reload_waiter_orders('diproses'), width=8).pack(side='left', padx=2) + ttk.Button(filter_frame, text="Diproses", command=lambda: self.reload_waiter_orders('menunggu'), width=10).pack(side='left', padx=2) ttk.Button(filter_frame, text="Selesai", command=lambda: self.reload_waiter_orders('selesai'), width=8).pack(side='left', padx=2) # Treeview pesanan @@ -2917,17 +2914,13 @@ class App: detail_scroll = ttk.Scrollbar(text_frame, orient='vertical', command=self.waiter_detail_text.yview) self.waiter_detail_text.configure(yscrollcommand=detail_scroll.set) - # PACK SCROLLBAR DULUAN detail_scroll.pack(side='right', fill='y') - - # BARU TEXT AREA self.waiter_detail_text.pack(side='left', fill='both', expand=True) - # TOMBOL AKSI - action_frame = ttk.LabelFrame(parent, text="🎯 Ubah Status Pesanan", padding=12) + # TOMBOL AKSI - DENGAN STATE MANAGEMENT + action_frame = ttk.LabelFrame(parent, text="🎯 Update Status Pesanan", padding=12) action_frame.pack(fill='x', padx=10, pady=8) - # ✅ JIKA READ-ONLY, TAMPILKAN WARNING & DISABLE TOMBOL if readonly: warning_label = ttk.Label( action_frame, @@ -2937,53 +2930,88 @@ class App: foreground='red', justify='center' ) - - warning_label.grid(row=2, column=0, columnspan=2, pady=10) + warning_label.pack(pady=10) - # Grid 2x2 untuk 4 tombol - btn1 = ttk.Button( - action_frame, - text="✅ Terima (Pending → Menunggu)", + # Grid untuk 2 tombol + btn_frame = ttk.Frame(action_frame) + btn_frame.pack(fill='x', pady=10) + + self.waiter_btn_terima = tk.Button( + btn_frame, + text="✅ Terima Pesanan (Pending → Diproses)", command=lambda: self.update_order_status('menunggu') if not readonly else None, - width=35, - state='disabled' if readonly else 'normal' + font=("Arial", 10, "bold"), + relief='raised', + cursor='hand2', + state='disabled' if readonly else 'normal', + bg='#4CAF50', + fg='white', + activebackground='#45a049', + padx=10, + pady=10 ) - btn1.grid(row=0, column=0, padx=4, pady=4, sticky='ew') + self.waiter_btn_terima.pack(fill='x', padx=5, pady=5) - btn2 = ttk.Button( - action_frame, - text="🍳 Proses (Menunggu → Diproses)", - command=lambda: self.update_order_status('diproses') if not readonly else None, - width=35, - state='disabled' if readonly else 'normal' - ) - btn2.grid(row=0, column=1, padx=4, pady=4, sticky='ew') - - btn3 = ttk.Button( - action_frame, - text="🍽️ Selesai (Diproses → Selesai)", + self.waiter_btn_proses = tk.Button( + btn_frame, + text="🍳 Pesanan Siap Disajikan (Diproses → Selesai)", command=lambda: self.update_order_status('selesai') if not readonly else None, - width=35, - state='disabled' if readonly else 'normal' + font=("Arial", 10, "bold"), + relief='raised', + cursor='hand2', + state='disabled' if readonly else 'normal', + bg='#2196F3', + fg='white', + activebackground='#0b7dda', + padx=10, + pady=10 ) - btn3.grid(row=1, column=0, padx=4, pady=4, sticky='ew') + self.waiter_btn_proses.pack(fill='x', padx=5, pady=5) - btn4 = ttk.Button( + # ===== INFO BOX - STATUS FLOW ===== + info_box = tk.Frame( action_frame, - text="💰 Dibayar (Selesai → Dibayar)", - command=lambda: self.update_order_status('dibayar') if not readonly else None, - width=35, - state='disabled' if readonly else 'normal' + bg='#E3F2FD', + relief='solid', + borderwidth=2, + padx=15, + pady=12 ) - btn4.grid(row=1, column=1, padx=4, pady=4, sticky='ew') + info_box.pack(fill='x', pady=10) - action_frame.columnconfigure(0, weight=1) - action_frame.columnconfigure(1, weight=1) + tk.Label( + info_box, + text="📌 ALUR STATUS PESANAN", + font=("Arial", 10, "bold"), + bg='#E3F2FD', + fg='#1565C0' + ).pack(anchor='w', pady=(0, 8)) + + tk.Label( + info_box, + text="Pending (Baru) → Diproses (Sedang Disiapkan) → Selesai (Siap Disajikan)", + font=("Arial", 9, "bold"), + bg='#E3F2FD', + fg='#0D47A1', + wraplength=500, + justify='center' + ).pack(anchor='w', pady=(0, 8)) + + tk.Label( + info_box, + text="💡 Tombol akan otomatis berubah warna sesuai status pesanan.\n" + "Tombol berwarna cerah bisa diklik, yang abu-abu tidak.", + font=("Arial", 8), + bg='#E3F2FD', + fg='#424242', + justify='center', + wraplength=500 + ).pack(anchor='w') # Load data self.reload_waiter_orders() - + def reload_waiter_orders(self, status_filter=None): """Load pesanan untuk waiter""" # Clear tree @@ -3062,7 +3090,10 @@ class App: self.waiter_detail_text.insert('1.0', detail_text) def update_order_status(self, new_status): - """Update status pesanan yang dipilih""" + """Update status pesanan dengan visual feedback + + Status Flow: pending → menunggu → selesai + """ sel = self.waiter_tree.selection() if not sel: messagebox.showwarning("Pilih Pesanan", "Pilih pesanan terlebih dahulu") @@ -3072,53 +3103,247 @@ class App: transaksi_id = item[0] current_status = item[4] - # Validasi flow status yang BENAR + # Validasi flow status valid_transitions = { - 'pending': ['menunggu'], - 'menunggu': ['diproses'], - 'diproses': ['selesai'], - 'selesai': ['dibayar'] + 'pending': ['menunggu'], # Pending → Diproses + 'menunggu': ['selesai'], # Diproses → Selesai + 'selesai': [] # Selesai = FINAL } - # Cek apakah current status valid + # Cek current status valid if current_status not in valid_transitions: - messagebox.showerror("Error", f"Status '{current_status}' sudah final, tidak bisa diubah lagi") + messagebox.showerror( + "Error", + f"❌ Status '{current_status}' sudah final!\n\n" + f"Pesanan ini sudah siap disajikan." + ) return - # Cek apakah new_status valid untuk current_status + # Cek status sudah final + if not valid_transitions[current_status]: + messagebox.showerror( + "Status Final", + f"❌ Pesanan sudah mencapai status 'SELESAI'\n\n" + f"Tidak ada lagi yang perlu dilakukan waiter." + ) + return + + # Cek new_status valid if new_status not in valid_transitions[current_status]: expected = ', '.join(valid_transitions[current_status]) - messagebox.showwarning("Status Tidak Valid", + messagebox.showwarning( + "Status Tidak Valid", f"❌ Tidak bisa ubah dari '{current_status}' ke '{new_status}'.\n\n" f"✅ Status yang benar setelah '{current_status}' adalah:\n" - f" → {expected}") + f" → {expected}" + ) return + # Mapping status untuk pesan + status_messages = { + 'pending': 'Pesanan Baru', + 'menunggu': 'Sedang Disiapkan', + 'selesai': 'Siap Disajikan' + } + + current_msg = status_messages.get(current_status, current_status.upper()) + new_msg = status_messages.get(new_status, new_status.upper()) + # Konfirmasi - if not messagebox.askyesno("Konfirmasi", - f"Ubah status pesanan #{transaksi_id}\n\n" - f"Dari: {current_status.upper()}\n" - f"Ke: {new_status.upper()}\n\n" - f"Lanjutkan?"): + confirm_msg = f"Ubah status pesanan #{transaksi_id}?\n\n" + confirm_msg += f"Dari: {current_msg}\n" + confirm_msg += f"Ke: {new_msg}\n\n" + + if new_status == 'selesai': + confirm_msg += "⚠️ Setelah ini, pesanan siap disajikan ke customer.\n" + confirm_msg += "Customer dapat langsung diminta untuk bayar." + elif new_status == 'menunggu': + confirm_msg += "Pesanan akan dimasukkan ke daftar yang sedang disiapkan." + + confirm_msg += "\n\nLanjutkan?" + + if not messagebox.askyesno("Konfirmasi", confirm_msg): return - # Update status + # Update status di database success = transaksi_update_status(transaksi_id, new_status) if success: - messagebox.showinfo("✅ Berhasil", f"Status berhasil diubah menjadi '{new_status.upper()}'") + # Pesan sukses + if new_status == 'menunggu': + success_msg = ( + f"✅ Pesanan #{transaksi_id} sedang disiapkan.\n\n" + f"Tim dapur: mulai siapkan pesanan ini!" + ) + elif new_status == 'selesai': + success_msg = ( + f"✅ Pesanan #{transaksi_id} siap disajikan!\n\n" + f"Silakan bawa ke meja customer." + ) + else: + success_msg = f"✅ Status berhasil diubah menjadi '{new_status.upper()}'" + + messagebox.showinfo("Berhasil", success_msg) + + # ✅ RELOAD DAN UPDATE BUTTON STATE self.reload_waiter_orders() + self.update_waiter_button_state() + # Auto select row yang sama for item_id in self.waiter_tree.get_children(): if self.waiter_tree.item(item_id)['values'][0] == transaksi_id: self.waiter_tree.selection_set(item_id) self.waiter_tree.see(item_id) - # Trigger event untuk reload detail self.on_waiter_select(None) break else: messagebox.showerror("❌ Gagal", "Gagal mengubah status pesanan") + def update_waiter_button_state(self): + """Update state tombol berdasarkan status pesanan yang dipilih + + Logika: + - Pending: Tombol TERIMA aktif, tombol PROSES disabled + - Diproses: Tombol TERIMA disabled, tombol PROSES aktif + - Selesai: Semua tombol disabled (pesanan sudah final) + """ + sel = self.waiter_tree.selection() + + if not sel: + # Tidak ada pesanan dipilih: semua disabled + self.waiter_btn_terima.config(state='disabled', bg='#cccccc', fg='#666666') + self.waiter_btn_proses.config(state='disabled', bg='#cccccc', fg='#666666') + return + + item = self.waiter_tree.item(sel)['values'] + current_status = item[4] # Status adalah field ke-4 (index 4) + + # ===== LOGIC UNTUK SET STATE TOMBOL ===== + + if current_status == 'pending': + # Status PENDING: Tombol TERIMA aktif, PROSES disabled + + # Tombol TERIMA - AKTIF (Hijau) + self.waiter_btn_terima.config( + state='normal', + bg='#4CAF50', + fg='white', + cursor='hand2', + relief='raised', + activebackground='#45a049' + ) + + # Tombol PROSES - DISABLED (Gelap) + self.waiter_btn_proses.config( + state='disabled', + bg='#A9A9A9', + fg='#555555', + cursor='arrow', + relief='sunken' + ) + + elif current_status == 'menunggu': + # Status DIPROSES: Tombol TERIMA disabled, PROSES aktif + + # Tombol TERIMA - DISABLED + self.waiter_btn_terima.config( + state='disabled', + bg='#A9A9A9', + fg='#555555', + cursor='arrow', + relief='sunken' + ) + + # Tombol PROSES - AKTIF (Biru) + self.waiter_btn_proses.config( + state='normal', + bg='#2196F3', + fg='white', + cursor='hand2', + relief='raised', + activebackground='#0b7dda' + ) + + elif current_status == 'selesai': + # Status SELESAI: Semua tombol DISABLED (FINAL STATE) + + self.waiter_btn_terima.config( + state='disabled', + bg='#D3D3D3', + fg='#777777', + cursor='arrow', + relief='sunken' + ) + + self.waiter_btn_proses.config( + state='disabled', + bg='#D3D3D3', + fg='#777777', + cursor='arrow', + relief='sunken' + ) + + + # ======================================== + # ON WAITER SELECT - CALL UPDATE BUTTON STATE + # ======================================== + + def on_waiter_select(self, event): + """Tampilkan detail pesanan + UPDATE BUTTON STATE""" + sel = self.waiter_tree.selection() + if not sel: + return + + item = self.waiter_tree.item(sel)['values'] + transaksi_id = item[0] + + # Get detail transaksi + transaksi_data = transaksi_get(transaksi_id) + if not transaksi_data: + return + + tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data + detail_items = detail_transaksi_list(transaksi_id) + + # Format detail + detail_text = f"═══════════════════════════════════════════════\n" + detail_text += f"TRANSAKSI ID: {tid}\n" + detail_text += f"═══════════════════════════════════════════════\n\n" + detail_text += f"User ID : {uid}\n" + detail_text += f"Nomor Meja : {meja}\n" + detail_text += f"Status : {status.upper()}\n" + detail_text += f"Tanggal : {tanggal}\n" + detail_text += f"Kode Promo : {promo_code if promo_code else '-'}\n\n" + + detail_text += f"───────────────────────────────────────────────\n" + detail_text += f"ITEM PESANAN:\n" + detail_text += f"───────────────────────────────────────────────\n" + + for detail in detail_items: + did, mid, qty, harga, subtotal_item = detail + menu_data = menu_get(mid) + if menu_data: + _, nama, kategori, _, _, _, _, _ = menu_data + detail_text += f"• {nama} ({kategori})\n" + detail_text += f" {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n\n" + + detail_text += f"───────────────────────────────────────────────\n" + detail_text += f"Subtotal : Rp {subtotal:,.0f}\n" + detail_text += f"Diskon Item : Rp {item_disc:,.0f}\n" + detail_text += f"Diskon Promo : Rp {promo_disc:,.0f}\n" + detail_text += f"───────────────────────────────────────────────\n" + detail_text += f"TOTAL BAYAR : Rp {total:,.0f}\n" + detail_text += f"═══════════════════════════════════════════════\n" + + # Tampilkan di text widget + self.waiter_detail_text.delete('1.0', tk.END) + self.waiter_detail_text.insert('1.0', detail_text) + + # ✅ UPDATE BUTTON STATE SESUAI STATUS PESANAN + self.update_waiter_button_state() + + + # Wilayah dikuasai Favorite diff --git a/menu.csv b/menu.csv index 4f3df81..200e799 100644 --- a/menu.csv +++ b/menu.csv @@ -1,13 +1,13 @@ id,nama,kategori,harga,stok,foto,tersedia,item_discount_pct 1,Americano,Minuman,20000.0,7,img/americano.jpg,1,0.0 -2,Latte,Minuman,25000.0,4,img/latte.jpg,1,10.0 -3,Banana Cake,Dessert,30000.0,8,img/banana_cake.jpg,1,4.0 +2,Latte,Minuman,25000.0,3,img/latte.jpg,1,10.0 +3,Banana Cake,Dessert,30000.0,7,img/banana_cake.jpg,1,4.0 4,Nasi Goreng,Makanan,25000.0,9,img/nasi_goreng.jpg,1,0.0 5,Nasi Kuning,Makanan,18000.0,7,img/Nasi-Kuning.jpg,1,5.0 6,Strawberry Milkshake,Minuman,19000.0,5,img/strawberry_milkshake.jpg,1,0.0 -7,Coconut Matcha,Minuman,21000.0,12,img/coconut_matcha.jpg,1,5.0 -8,Beef Yakiniku,Makanan,25000.0,12,img/Beef-Yakiniku.jpg,1,4.0 +7,Coconut Matcha,Minuman,21000.0,11,img/coconut_matcha.jpg,1,5.0 +8,Beef Yakiniku,Makanan,25000.0,11,img/Beef-Yakiniku.jpg,1,4.0 9,Osaka Curry,Makanan,30000.0,14,img/osaka_curry.jpg,1,10.0 -10,Choco Pudding,Dessert,20000.0,16,img/chocolate-pudding.jpg,1,8.0 +10,Choco Pudding,Dessert,20000.0,15,img/chocolate-pudding.jpg,1,8.0 11,Totoro Dango,Dessert,18000.0,9,img/dango.jpg,1,4.0 12,Cheese Cake,Dessert,32000.0,9,img/cheese-cake.jpg,1,6.0 diff --git a/pembayaran.csv b/pembayaran.csv index 8a7b272..96908bf 100644 --- a/pembayaran.csv +++ b/pembayaran.csv @@ -4,3 +4,6 @@ id,transaksi_id,metode_pembayaran,jumlah_bayar,status_pembayaran,tanggal_bayar,s 3,9,qris,63855.0,sukses,2025-12-14 09:52:46, 4,10,cash,25000.0,sukses,2025-12-14 10:05:01, 5,11,ewallet-gopay,48420.0,sukses,2025-12-14 10:52:50, +6,13,qris,75300.0,sukses,2025-12-14 21:17:59, +7,12,cash,100000.0,sukses,2025-12-14 21:46:15, +8,14,cash,100000.0,sukses,2025-12-14 21:46:20, diff --git a/struk/STR-12-20251214_214615.txt b/struk/STR-12-20251214_214615.txt new file mode 100644 index 0000000..4a8d246 --- /dev/null +++ b/struk/STR-12-20251214_214615.txt @@ -0,0 +1,27 @@ +═════════════════════════════════════════ + TOTORO CAFE + Jl. Raya Totoro No. 123, Surabaya + Telp: 031-123456 +═════════════════════════════════════════ + +No. Struk : STR-12-20251214 +Tanggal : 14/12/2025 21:12 +Meja : 1 +Kasir : kasir +Pembayaran : CASH +───────────────────────────────────────── +ITEM PESANAN: +───────────────────────────────────────── +Coconut Matcha + 1 x Rp 21,000 Rp 21,000 +───────────────────────────────────────── +Subtotal : Rp 21,000 +Diskon Item : Rp 1,050 +───────────────────────────────────────── +TOTAL BAYAR : Rp 19,950 +Bayar : Rp 100,000 +Kembalian : Rp 80,050 +═════════════════════════════════════════ + TERIMA KASIH ATAS KUNJUNGAN ANDA + SAMPAI JUMPA LAGI! +═════════════════════════════════════════ diff --git a/struk/STR-13-20251214_211759.txt b/struk/STR-13-20251214_211759.txt new file mode 100644 index 0000000..b923d56 --- /dev/null +++ b/struk/STR-13-20251214_211759.txt @@ -0,0 +1,29 @@ +═════════════════════════════════════════ + TOTORO CAFE + Jl. Raya Totoro No. 123, Surabaya + Telp: 031-123456 +═════════════════════════════════════════ + +No. Struk : STR-13-20251214 +Tanggal : 14/12/2025 21:14 +Meja : 2 +Kasir : kasir +Pembayaran : QRIS +───────────────────────────────────────── +ITEM PESANAN: +───────────────────────────────────────── +Beef Yakiniku + 1 x Rp 25,000 Rp 25,000 +Latte + 1 x Rp 25,000 Rp 25,000 +Banana Cake + 1 x Rp 30,000 Rp 30,000 +───────────────────────────────────────── +Subtotal : Rp 80,000 +Diskon Item : Rp 4,700 +───────────────────────────────────────── +TOTAL BAYAR : Rp 75,300 +═════════════════════════════════════════ + TERIMA KASIH ATAS KUNJUNGAN ANDA + SAMPAI JUMPA LAGI! +═════════════════════════════════════════ diff --git a/struk/STR-14-20251214_214620.txt b/struk/STR-14-20251214_214620.txt new file mode 100644 index 0000000..5ee3694 --- /dev/null +++ b/struk/STR-14-20251214_214620.txt @@ -0,0 +1,27 @@ +═════════════════════════════════════════ + TOTORO CAFE + Jl. Raya Totoro No. 123, Surabaya + Telp: 031-123456 +═════════════════════════════════════════ + +No. Struk : STR-14-20251214 +Tanggal : 14/12/2025 21:43 +Meja : 9 +Kasir : kasir +Pembayaran : CASH +───────────────────────────────────────── +ITEM PESANAN: +───────────────────────────────────────── +Choco Pudding + 1 x Rp 20,000 Rp 20,000 +───────────────────────────────────────── +Subtotal : Rp 20,000 +Diskon Item : Rp 1,600 +───────────────────────────────────────── +TOTAL BAYAR : Rp 18,400 +Bayar : Rp 100,000 +Kembalian : Rp 81,600 +═════════════════════════════════════════ + TERIMA KASIH ATAS KUNJUNGAN ANDA + SAMPAI JUMPA LAGI! +═════════════════════════════════════════ diff --git a/transaksi.csv b/transaksi.csv index 8f5e928..7e79130 100644 --- a/transaksi.csv +++ b/transaksi.csv @@ -10,3 +10,6 @@ id,user_id,nomor_meja,total,status,promo_code,subtotal,item_discount,promo_disco 9,4,3,63855.0,dibayar,OPENING,76000.0,5050.0,7095.0,2025-12-14 09:50:02 10,4,5,23347.5,dibayar,MERDEKA,46000.0,3550.0,19102.5,2025-12-14 10:03:04 11,4,1,48420.0,dibayar,OPENING,55000.0,1200.0,5380.0,2025-12-14 10:35:35 +12,4,1,19950.0,dibayar,,21000.0,1050.0,0.0,2025-12-14 21:12:55 +13,4,2,75300.0,dibayar,,80000.0,4700.0,0.0,2025-12-14 21:14:46 +14,4,9,18400.0,dibayar,,20000.0,1600.0,0.0,2025-12-14 21:43:51