Metode Pembayaran

This commit is contained in:
Jevinca Marvella 2025-12-13 17:45:53 +07:00
parent 123269fc23
commit 911e950085
6 changed files with 816 additions and 499 deletions

View File

@ -1,2 +1,4 @@
id,transaksi_id,menu_id,qty,harga_satuan,subtotal_item
1,1,1,1,20000.0,20000.0
1,2,1,1,20000.0,20000.0
2,3,1,1,20000.0,20000.0
3,4,1,1,20000.0,20000.0

1 id transaksi_id menu_id qty harga_satuan subtotal_item
2 1 1 2 1 1 20000.0 20000.0
3 2 3 1 1 20000.0 20000.0
4 3 4 1 1 20000.0 20000.0

View File

@ -1,2 +1,2 @@
user_id,menu_id,order_count,last_ordered
4,1,1,2025-12-13 15:09:50
4,1,3,2025-12-13 17:22:52

1 user_id menu_id order_count last_ordered
2 4 1 1 3 2025-12-13 15:09:50 2025-12-13 17:22:52

373
main.py
View File

@ -638,16 +638,129 @@ def apply_discounts_and_promo(cart_items, promo_code=None):
# === FUNGSI FAVORITE ===
def favorite_update(user_id, menu_id):
"""Update atau tambah favorite count untuk user tertentu"""
from datetime import datetime
rows = read_all(FAVORITE_CSV)
found = False
for r in rows:
if r.get("user_id") == str(user_id) and r.get("menu_id") == str(menu_id):
try:
count = int(r.get("order_count") or 0)
except:
count = 0
r["order_count"] = str(count + 1)
r["last_ordered"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
found = True
break
if not found:
rows.append({
"user_id": str(user_id),
"menu_id": str(menu_id),
"order_count": "1",
"last_ordered": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
})
write_all(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"], rows)
def favorite_list(user_id, limit=5):
"""Ambil menu favorit user, sorted by order_count descending"""
rows = read_all(FAVORITE_CSV)
out = []
for r in rows:
if r.get("user_id") == str(user_id):
try:
mid = int(r.get("menu_id") or 0)
except:
mid = r.get("menu_id")
try:
count = int(r.get("order_count") or 0)
except:
count = 0
last_ordered = r.get("last_ordered")
out.append((mid, count, last_ordered))
out.sort(key=lambda x: x[1], reverse=True)
if limit:
out = out[:limit]
return out
# === FUNGSI PEMBAYARAN ===
def pembayaran_add(transaksi_id, metode_pembayaran, jumlah_bayar, status_pembayaran='sukses', struk=''):
"""Simpan data pembayaran baru"""
from datetime import datetime
rows = read_all(PEMBAYARAN_CSV)
new_id = next_int_id(rows, "id")
tanggal_bayar = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
rows.append({
"id": new_id,
"transaksi_id": str(transaksi_id),
"metode_pembayaran": metode_pembayaran,
"jumlah_bayar": str(float(jumlah_bayar)),
"status_pembayaran": status_pembayaran,
"tanggal_bayar": tanggal_bayar,
"struk": struk
})
write_all(PEMBAYARAN_CSV, ["id", "transaksi_id", "metode_pembayaran", "jumlah_bayar", "status_pembayaran", "tanggal_bayar", "struk"], rows)
return new_id
def pembayaran_get_by_transaksi(transaksi_id):
"""Ambil data pembayaran berdasarkan transaksi_id"""
rows = read_all(PEMBAYARAN_CSV)
for r in rows:
if r.get("transaksi_id") == str(transaksi_id):
try:
pid = int(r.get("id") or 0)
except:
pid = r.get("id")
try:
jumlah = float(r.get("jumlah_bayar") or 0.0)
except:
jumlah = 0.0
return (pid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar"), r.get("struk"))
return None
# === FUNGSI MEJA ===
def meja_update_status(nomor_meja, new_status, transaksi_id=""):
"""Update status meja (kosong/terisi) dan link ke transaksi"""
rows = read_all(MEJA_CSV)
found = False
for r in rows:
if r.get("nomor_meja") == str(nomor_meja):
r["status"] = new_status
r["transaksi_id"] = str(transaksi_id) if transaksi_id else ""
found = True
break
if found:
write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows)
return True
return False
def meja_tutup(nomor_meja):
"""Tutup meja (set ke kosong)"""
return meja_update_status(nomor_meja, "kosong", "")
# Wilayah dikuasai UI
@ -2119,7 +2232,150 @@ def favorite_all(limit=10):
return out
# FITUR PEMBAYARAN
# WILAYAH DIKUASAI PEMBAYARAN & MEJA
# === FUNGSI PEMBAYARAN ===
def pembayaran_add(transaksi_id, metode_pembayaran, jumlah_bayar, status_pembayaran='sukses', struk=''):
"""Simpan data pembayaran baru"""
from datetime import datetime
rows = read_all(PEMBAYARAN_CSV)
new_id = next_int_id(rows, "id")
tanggal_bayar = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
rows.append({
"id": new_id,
"transaksi_id": str(transaksi_id),
"metode_pembayaran": metode_pembayaran,
"jumlah_bayar": str(float(jumlah_bayar)),
"status_pembayaran": status_pembayaran,
"tanggal_bayar": tanggal_bayar,
"struk": struk
})
write_all(PEMBAYARAN_CSV, ["id", "transaksi_id", "metode_pembayaran", "jumlah_bayar", "status_pembayaran", "tanggal_bayar", "struk"], rows)
return new_id
def pembayaran_get_by_transaksi(transaksi_id):
"""Ambil data pembayaran berdasarkan transaksi_id"""
rows = read_all(PEMBAYARAN_CSV)
for r in rows:
if r.get("transaksi_id") == str(transaksi_id):
try:
pid = int(r.get("id") or 0)
except:
pid = r.get("id")
try:
jumlah = float(r.get("jumlah_bayar") or 0.0)
except:
jumlah = 0.0
return (pid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar"), r.get("struk"))
return None
def pembayaran_list(status=None, metode=None):
"""Ambil semua pembayaran dengan filter opsional"""
rows = read_all(PEMBAYARAN_CSV)
out = []
for r in rows:
if status and r.get("status_pembayaran") != status:
continue
if metode and r.get("metode_pembayaran") != metode:
continue
try:
pid = int(r.get("id") or 0)
except:
pid = r.get("id")
try:
tid = int(r.get("transaksi_id") or 0)
except:
tid = r.get("transaksi_id")
try:
jumlah = float(r.get("jumlah_bayar") or 0.0)
except:
jumlah = 0.0
out.append((pid, tid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar")))
out.sort(key=lambda x: int(x[0]), reverse=True)
return out
# === FUNGSI MEJA ===
def meja_list(status=None):
"""Ambil daftar meja dengan filter status opsional"""
rows = read_all(MEJA_CSV)
out = []
for r in rows:
if status and r.get("status") != status:
continue
try:
nomor = int(r.get("nomor_meja") or 0)
except:
nomor = r.get("nomor_meja")
transaksi_id = r.get("transaksi_id") or ""
out.append((nomor, r.get("status"), transaksi_id))
out.sort(key=lambda x: int(x[0]) if isinstance(x[0], int) else 0)
return out
def meja_get(nomor_meja):
"""Ambil data meja berdasarkan nomor"""
rows = read_all(MEJA_CSV)
for r in rows:
if r.get("nomor_meja") == str(nomor_meja):
try:
nomor = int(r.get("nomor_meja") or 0)
except:
nomor = r.get("nomor_meja")
transaksi_id = r.get("transaksi_id") or ""
return (nomor, r.get("status"), transaksi_id)
return None
def meja_update_status(nomor_meja, new_status, transaksi_id=""):
"""Update status meja (kosong/terisi) dan link ke transaksi"""
rows = read_all(MEJA_CSV)
found = False
for r in rows:
if r.get("nomor_meja") == str(nomor_meja):
r["status"] = new_status
r["transaksi_id"] = str(transaksi_id) if transaksi_id else ""
found = True
break
if found:
write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows)
return True
return False
def meja_tutup(nomor_meja):
"""Tutup meja (set ke kosong)"""
return meja_update_status(nomor_meja, "kosong", "")
def meja_buka(nomor_meja, transaksi_id):
"""Buka meja (set ke terisi dengan transaksi_id)"""
return meja_update_status(nomor_meja, "terisi", transaksi_id)
def build_payment_tab(self, parent):
"""Tab pembayaran untuk kasir & admin"""
@ -2132,18 +2388,25 @@ def favorite_all(limit=10):
ttk.Label(header, text="💰 Pembayaran Transaksi", font=("Arial", 14, "bold")).pack(side='left')
ttk.Button(header, text="🔄 Refresh", command=self.reload_payment_orders).pack(side='right', padx=6)
# Split 2 panel: kiri = daftar transaksi, kanan = form pembayaran
left = ttk.LabelFrame(parent, text="📋 Transaksi Siap Dibayar (Status: Selesai)", padding=10)
left.pack(side='left', fill='both', expand=True, padx=10, pady=6)
# Container utama
main_container = ttk.Frame(parent)
main_container.pack(fill='both', expand=True, padx=10, pady=6)
right = ttk.LabelFrame(parent, text="💳 Form Pembayaran", padding=10)
right.pack(side='right', fill='both', expand=True, padx=10, pady=6)
# === PANEL KIRI: Daftar Transaksi ===
left = ttk.LabelFrame(main_container, text="📋 Transaksi Siap Dibayar (Status: Selesai)", padding=10)
left.pack(side='left', fill='both', expand=True, padx=(0, 5))
# === PANEL KIRI: Daftar Transaksi Selesai ===
# Treeview transaksi dengan scrollbar
tree_frame = ttk.Frame(left)
tree_frame.pack(fill='both', expand=True)
tree_scroll = ttk.Scrollbar(tree_frame, orient='vertical')
tree_scroll.pack(side='right', fill='y')
# Treeview transaksi
cols = ("ID", "Meja", "Total", "Status", "Tanggal")
self.payment_tree = ttk.Treeview(left, columns=cols, show='headings', height=12)
self.payment_tree = ttk.Treeview(tree_frame, columns=cols, show='headings', height=10, yscrollcommand=tree_scroll.set)
tree_scroll.config(command=self.payment_tree.yview)
self.payment_tree.heading("ID", text="ID")
self.payment_tree.heading("Meja", text="Meja")
@ -2155,18 +2418,18 @@ def favorite_all(limit=10):
self.payment_tree.column("Meja", width=60)
self.payment_tree.column("Total", width=100)
self.payment_tree.column("Status", width=80)
self.payment_tree.column("Tanggal", width=140)
self.payment_tree.column("Tanggal", width=120)
self.payment_tree.pack(fill='both', expand=True, pady=6)
# Bind event
self.payment_tree.pack(side='left', fill='both', expand=True)
self.payment_tree.bind("<<TreeviewSelect>>", self.on_payment_select)
# Detail transaksi
detail_frame = ttk.Frame(left)
detail_frame.pack(fill='x', pady=6)
detail_frame.pack(fill='x', pady=(10, 0))
self.payment_detail_text = tk.Text(detail_frame, height=8, font=("Courier New", 8), wrap='word')
ttk.Label(detail_frame, text="Detail Transaksi:", font=("Arial", 9, "bold")).pack(anchor='w')
self.payment_detail_text = tk.Text(detail_frame, height=6, font=("Courier New", 8), wrap='word')
detail_scroll = ttk.Scrollbar(detail_frame, orient='vertical', command=self.payment_detail_text.yview)
self.payment_detail_text.configure(yscrollcommand=detail_scroll.set)
@ -2174,10 +2437,50 @@ def favorite_all(limit=10):
detail_scroll.pack(side='right', fill='y')
# === PANEL KANAN: Form Pembayaran ===
right_outer = ttk.LabelFrame(main_container, text="💳 Form Pembayaran", padding=5)
right_outer.pack(side='right', fill='both', expand=True, padx=(5, 0))
# Info transaksi terpilih
# Frame wrapper untuk canvas
wrapper = ttk.Frame(right_outer)
wrapper.pack(fill='both', expand=True)
# Scrollbar
scrollbar = ttk.Scrollbar(wrapper, orient='vertical')
scrollbar.pack(side='right', fill='y')
# Canvas dengan HEIGHT FIXED
canvas = tk.Canvas(wrapper, yscrollcommand=scrollbar.set, highlightthickness=0, height=450)
canvas.pack(side='left', fill='both', expand=True)
scrollbar.config(command=canvas.yview)
# Frame konten
right = ttk.Frame(canvas)
canvas_window = canvas.create_window((0, 0), window=right, anchor='nw')
# Update scroll region
def configure_scroll(event):
canvas.configure(scrollregion=canvas.bbox('all'))
right.bind('<Configure>', configure_scroll)
# Update width
def configure_canvas(event):
canvas.itemconfig(canvas_window, width=event.width)
canvas.bind('<Configure>', configure_canvas)
# Mouse wheel
def on_mousewheel(event):
canvas.yview_scroll(-1 * int(event.delta / 120), "units")
canvas.bind_all('<MouseWheel>', on_mousewheel)
# === ISI FORM ===
# Info transaksi
info_frame = ttk.Frame(right)
info_frame.pack(fill='x', pady=10)
info_frame.pack(fill='x', pady=15, padx=15)
self.selected_transaksi_label = ttk.Label(info_frame, text="Belum ada transaksi dipilih", font=("Arial", 10, "bold"), foreground='red')
self.selected_transaksi_label.pack()
@ -2185,31 +2488,39 @@ def favorite_all(limit=10):
self.selected_total_label = ttk.Label(info_frame, text="Total: Rp 0", font=("Arial", 12, "bold"), foreground='green')
self.selected_total_label.pack(pady=4)
ttk.Separator(right, orient='horizontal').pack(fill='x', pady=10)
ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15)
# Pilih metode pembayaran
# Metode pembayaran
method_frame = ttk.Frame(right)
method_frame.pack(fill='x', pady=10)
method_frame.pack(fill='x', pady=10, padx=15)
ttk.Label(method_frame, text="💳 Pilih Metode Pembayaran:", font=("Arial", 10, "bold")).pack(anchor='w', pady=4)
ttk.Label(method_frame, text="💳 Pilih Metode Pembayaran:", font=("Arial", 10, "bold")).pack(anchor='w', pady=6)
self.payment_method_var = tk.StringVar(value='cash')
ttk.Radiobutton(method_frame, text="💵 Cash", variable=self.payment_method_var, value='cash', command=self.on_payment_method_change).pack(anchor='w', pady=2)
ttk.Radiobutton(method_frame, text="📱 QRIS", variable=self.payment_method_var, value='qris', command=self.on_payment_method_change).pack(anchor='w', pady=2)
ttk.Radiobutton(method_frame, text="💳 E-Wallet (GoPay/OVO/Dana)", variable=self.payment_method_var, value='ewallet', command=self.on_payment_method_change).pack(anchor='w', pady=2)
ttk.Radiobutton(method_frame, text="💵 Cash", variable=self.payment_method_var, value='cash', command=self.on_payment_method_change).pack(anchor='w', pady=3)
ttk.Radiobutton(method_frame, text="📱 QRIS", variable=self.payment_method_var, value='qris', command=self.on_payment_method_change).pack(anchor='w', pady=3)
ttk.Radiobutton(method_frame, text="💳 E-Wallet (GoPay/OVO/Dana)", variable=self.payment_method_var, value='ewallet', command=self.on_payment_method_change).pack(anchor='w', pady=3)
ttk.Separator(right, orient='horizontal').pack(fill='x', pady=10)
ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15)
# Frame dinamis untuk input (berganti sesuai metode)
# Frame input dinamis
self.payment_input_frame = ttk.Frame(right)
self.payment_input_frame.pack(fill='both', expand=True, pady=10)
self.payment_input_frame.pack(fill='x', pady=10, padx=15)
# Init cash input (default)
self.build_cash_input()
# Tombol proses pembayaran
ttk.Button(right, text="✅ PROSES PEMBAYARAN", command=self.process_payment, style="Accent.TButton").pack(pady=15, ipadx=20, ipady=10)
ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15)
# Tombol
btn_frame = ttk.Frame(right)
btn_frame.pack(fill='x', pady=20, padx=15)
ttk.Button(btn_frame, text="✅ PROSES PEMBAYARAN", command=self.process_payment, style="Accent.TButton").pack(ipadx=30, ipady=10)
# PENTING: Tambah banyak space di bawah biar scrollbar muncul
for i in range(20):
ttk.Label(right, text="").pack()
# Load data
self.reload_payment_orders()
@ -2349,6 +2660,7 @@ def favorite_all(limit=10):
except:
self.cash_change_label.config(text="Kembalian: Rp 0", foreground='gray')
def build_qris_input(self):
"""Form input untuk pembayaran QRIS (simulasi QR Code)"""
ttk.Label(self.payment_input_frame, text="📱 Pembayaran QRIS", font=("Arial", 10, "bold")).pack(pady=6)
@ -2624,7 +2936,6 @@ def print_struk_simulation(self, struk):
# Done
if __name__ == "__main__":
init_db_csv()

View File

@ -1,5 +1,5 @@
id,nama,kategori,harga,stok,foto,tersedia,item_discount_pct
1,Americano,Minuman,20000,4,,1,0
1,Americano,Minuman,20000,1,,1,0
2,Latte,Minuman,25000,1,,1,10
3,Banana Cake,Dessert,30000,0,,0,0
4,Nasi Goreng,Makanan,35000,0,,0,0

1 id nama kategori harga stok foto tersedia item_discount_pct
2 1 Americano Minuman 20000 4 1 1 0
3 2 Latte Minuman 25000 1 1 10
4 3 Banana Cake Dessert 30000 0 0 0
5 4 Nasi Goreng Makanan 35000 0 0 0

View File

@ -1 +1,2 @@
id,transaksi_id,metode_pembayaran,jumlah_bayar,status_pembayaran,tanggal_bayar,struk
1,4,ewallet-gopay,20000.0,sukses,2025-12-13 17:42:14,

1 id transaksi_id metode_pembayaran jumlah_bayar status_pembayaran tanggal_bayar struk
2 1 4 ewallet-gopay 20000.0 sukses 2025-12-13 17:42:14

View File

@ -1,2 +1,5 @@
id,user_id,nomor_meja,total,status,promo_code,subtotal,item_discount,promo_discount,tanggal
1,4,1,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 15:09:50
2,4,1,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 16:17:53
3,4,3,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 16:33:41
4,4,2,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-13 17:22:52

1 id user_id nomor_meja total status promo_code subtotal item_discount promo_discount tanggal
2 1 4 1 20000.0 dibayar 20000.0 0.0 0.0 2025-12-13 15:09:50
3 2 4 1 20000.0 dibayar 20000.0 0.0 0.0 2025-12-13 16:17:53
4 3 4 3 20000.0 dibayar 20000.0 0.0 0.0 2025-12-13 16:33:41
5 4 4 2 20000.0 dibayar 20000.0 0.0 0.0 2025-12-13 17:22:52