38244-vm/pos.php
2026-02-07 11:39:06 +00:00

707 lines
37 KiB
PHP

<?php
declare(strict_types=1);
require_once 'db/config.php';
$current_branch_id = (int)($_GET['branch_id'] ?? 1);
$db = db();
$branches = $db->query("SELECT * FROM branches")->fetchAll();
$current_branch = array_filter($branches, fn($b) => $b['id'] == $current_branch_id);
$current_branch = reset($current_branch);
$products = $db->query("SELECT p.*, c.name as category_name FROM products p LEFT JOIN categories c ON p.category_id = c.id WHERE p.branch_id = $current_branch_id AND p.stock > 0 ORDER BY p.name ASC")->fetchAll();
$members = $db->query("SELECT * FROM members ORDER BY name ASC")->fetchAll();
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POS - Kasir</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
<style>
.pos-container { height: calc(100vh - 80px); }
.product-grid { height: 100%; overflow-y: auto; }
.cart-panel { height: 100%; display: flex; flex-direction: column; background: #fff; border-left: 1px solid #e2e8f0; }
.cart-items { flex-grow: 1; overflow-y: auto; background-color: #f8fafc; }
.product-card { cursor: pointer; transition: 0.2s; border: 1px solid transparent; }
.product-card:hover { border-color: var(--accent-color); background-color: #f0fdf4; }
.bg-member { background-color: #f0fdf4 !important; border-color: #10b981 !important; }
.cart-item {
background: white;
border-radius: 8px;
margin-bottom: 10px;
padding: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border: 1px solid #e2e8f0;
transition: all 0.2s;
}
.cart-item:hover {
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
border-color: #cbd5e1;
}
/* Print Styles */
#printSection { display: none; }
@media print {
body * { visibility: hidden; }
#printSection, #printSection * { visibility: visible; }
#printSection { display: block; position: absolute; left: 0; top: 0; width: 100%; }
.no-print { display: none !important; }
}
.receipt-container { width: 80mm; font-family: 'Courier New', Courier, monospace; font-size: 12px; }
.invoice-container { width: 100%; font-family: Arial, sans-serif; }
.qty-input {
width: 70px;
text-align: center;
border: 1px solid #cbd5e1;
border-radius: 6px;
padding: 4px;
font-weight: bold;
color: #1e293b;
}
.qty-input:focus {
outline: none;
border-color: #3b82f6;
ring: 2px rgba(59, 130, 246, 0.2);
}
</style>
</head>
<body class="bg-light">
<nav class="navbar navbar-light bg-white border-bottom p-3 no-print">
<div class="container-fluid">
<div class="d-flex align-items-center">
<a href="index.php?branch_id=<?php echo $current_branch_id; ?>" class="btn btn-outline-dark me-3">
<i class="bi bi-arrow-left"></i>
</a>
<span class="navbar-brand mb-0 h1">Kasir - <?php echo htmlspecialchars($current_branch['name']); ?></span>
</div>
<div class="ms-auto d-flex align-items-center">
<a href="vouchers.php?branch_id=<?php echo $current_branch_id; ?>" class="btn btn-outline-primary me-3">
<i class="bi bi-ticket-perforated me-1"></i> Voucher
</a>
<div class="text-end me-3">
<div class="small text-muted">Waktu Sekarang</div>
<div class="fw-bold" id="clock">00:00:00</div>
</div>
</div>
</div>
</nav>
<div class="container-fluid pos-container no-print">
<div class="row h-100">
<!-- Product Section -->
<div class="col-md-8 p-4 product-grid">
<div class="row mb-3">
<div class="col-md-8">
<div class="input-group">
<span class="input-group-text bg-white border-end-0"><i class="bi bi-search"></i></span>
<input type="text" id="productSearch" class="form-control border-start-0" placeholder="Cari produk atau scan barcode...">
</div>
</div>
<div class="col-md-4">
<select class="form-select" id="categoryFilter">
<option value="">Semua Kategori</option>
<?php
$categories = $db->query("SELECT * FROM categories")->fetchAll();
foreach($categories as $cat): ?>
<option value="<?php echo $cat['id']; ?>"><?php echo htmlspecialchars($cat['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="row g-3" id="productContainer">
<?php if (empty($products)): ?>
<div class="col-12 text-center py-5">
<i class="bi bi-box-seam display-1 text-muted"></i>
<p class="mt-3">Produk tidak tersedia. Silakan <a href="products.php?branch_id=<?php echo $current_branch_id; ?> ">tambah produk</a> terlebih dahulu.</p>
</div>
<?php else: ?>
<?php foreach ($products as $p): ?>
<div class="col-md-3 product-item" data-name="<?php echo strtolower(htmlspecialchars($p['name'])); ?>" data-category="<?php echo $p['category_id']; ?>">
<div class="card product-card h-100 shadow-sm border-0" onclick='addToCart(<?php echo json_encode($p); ?>)'>
<div class="card-body p-3">
<div class="small text-muted mb-1"><?php echo htmlspecialchars($p['category_name'] ?? 'Umum'); ?></div>
<h6 class="card-title mb-1"><?php echo htmlspecialchars($p['name']); ?></h6>
<div class="d-flex flex-column">
<div class="small text-muted text-decoration-line-through">Rp <?php echo number_format((float)$p['selling_price'], 0, ',', '.'); ?></div>
<div class="fw-bold text-success">Rp <?php echo number_format((float)$p['member_price'], 0, ',', '.'); ?> <span class="badge bg-soft-success text-success small" style="font-size: 10px;">MEMBER</span></div>
</div>
<div class="small text-muted mt-2">Stok: <?php echo $p['stock']; ?></div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- Cart Section -->
<div class="col-md-4 p-0 cart-panel shadow-sm">
<div class="p-3 border-bottom bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">Keranjang Belanja</h5>
<button class="btn btn-sm btn-outline-danger" onclick="clearCart()">Bersihkan</button>
</div>
<!-- Member Selection -->
<div class="p-3 border-bottom bg-light">
<label class="form-label small fw-bold mb-1">Pilih Member (Opsional)</label>
<select class="form-select mb-2" id="memberSelect" onchange="updateMember()">
<option value="">-- Pelanggan Umum --</option>
<?php foreach ($members as $m): ?>
<option value="<?php echo $m['id']; ?>" data-name="<?php echo htmlspecialchars($m['name']); ?>" data-code="<?php echo htmlspecialchars($m['code']); ?>" data-points="<?php echo $m['points']; ?>"><?php echo htmlspecialchars($m['name']); ?> (<?php echo htmlspecialchars($m['code']); ?>)</option>
<?php endforeach; ?>
</select>
<div id="memberInfo" class="mt-2 small d-none">
<div class="d-flex justify-content-between align-items-center">
<span>Poin: <span class="fw-bold" id="memberPointsText">0</span></span>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="usePoints" onchange="renderCart()">
<label class="form-check-label" for="usePoints">Tukar Poin</label>
</div>
</div>
<div id="pointsRedemptionInput" class="mt-2 d-none">
<input type="number" id="pointsToUse" class="form-control form-control-sm" placeholder="Jumlah poin..." oninput="renderCart()">
<small class="text-muted">1 Poin = Rp 1</small>
</div>
</div>
<!-- Voucher Section -->
<div class="mt-3">
<label class="form-label small fw-bold mb-1">Voucher Promo</label>
<div class="input-group input-group-sm">
<input type="text" id="voucherCode" class="form-control" placeholder="Kode voucher...">
<button class="btn btn-primary" type="button" onclick="applyVoucher()">Gunakan</button>
</div>
<div id="voucherStatus" class="small mt-1 d-none"></div>
</div>
</div>
<div class="cart-items p-3" id="cartItems">
<!-- Items injected by JS -->
</div>
<div class="p-3 border-top bg-light">
<div class="d-flex justify-content-between mb-2">
<span>Subtotal</span>
<span id="subtotal">Rp 0</span>
</div>
<div class="d-flex justify-content-between mb-2 text-success d-none" id="memberDiscountRow">
<span>Hemat Member</span>
<span id="memberSavings">- Rp 0</span>
</div>
<div class="d-flex justify-content-between mb-2 text-danger d-none" id="pointsDiscountRow">
<span>Tukar Poin</span>
<span id="pointsDiscount">- Rp 0</span>
</div>
<div class="d-flex justify-content-between mb-2 text-danger d-none" id="voucherDiscountRow">
<span>Diskon Voucher</span>
<span id="voucherDiscount">- Rp 0</span>
</div>
<div class="d-flex justify-content-between mb-2 text-info d-none" id="pointsEarnedRow">
<span>Poin Didapat</span>
<span id="pointsEarned">0 Pts</span>
</div>
<hr>
<div class="d-flex justify-content-between mb-3">
<h4 class="mb-0">Total Akhir</h4>
<h4 class="mb-0 text-primary" id="total">Rp 0</h4>
</div>
<button class="btn btn-primary w-100 py-3 fw-bold shadow" id="payBtn" disabled data-bs-toggle="modal" data-bs-target="#payModal">
PROSES PEMBAYARAN
</button>
</div>
</div>
</div>
</div>
<!-- Payment Modal -->
<div class="modal fade" id="payModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content border-0 shadow">
<div class="modal-header border-0">
<h5 class="modal-title">Selesaikan Pembayaran</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="text-center mb-4">
<h6 class="text-muted mb-1">Total yang Harus Dibayar</h6>
<h2 class="text-primary mb-0" id="modalTotal">Rp 0</h2>
</div>
<div class="mb-3">
<label class="form-label small fw-bold">Metode Pembayaran</label>
<div class="btn-group w-100" role="group">
<input type="radio" class="btn-check" name="payMethod" id="payCash" value="cash" checked onchange="togglePaymentInputs()">
<label class="btn btn-outline-primary" for="payCash">Tunai</label>
<input type="radio" class="btn-check" name="payMethod" id="payTransfer" value="transfer" onchange="togglePaymentInputs()">
<label class="btn btn-outline-primary" for="payTransfer">Transfer</label>
<input type="radio" class="btn-check" name="payMethod" id="payCredit" value="credit" onchange="togglePaymentInputs()">
<label class="btn btn-outline-primary" for="payCredit" id="payCreditLabel">Kredit</label>
</div>
</div>
<div id="cashInputs">
<div class="mb-3">
<label class="form-label small fw-bold">Pilihan Cepat</label>
<div class="row g-2">
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(50000)">50rb</button></div>
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(100000)">100rb</button></div>
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(150000)">150rb</button></div>
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(200000)">200rb</button></div>
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(250000)">250rb</button></div>
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(300000)">300rb</button></div>
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(400000)">400rb</button></div>
<div class="col-3"><button type="button" class="btn btn-outline-secondary w-100 btn-sm" onclick="setCash(500000)">500rb</button></div>
<div class="col-12"><button type="button" class="btn btn-secondary w-100 btn-sm" onclick="setCash('exact')">Uang Pas</button></div>
</div>
</div>
<div class="mb-3">
<label class="form-label small fw-bold">Uang Diterima</label>
<input type="number" id="cashReceived" class="form-control form-control-lg" placeholder="Masukkan jumlah...">
</div>
<div class="alert alert-secondary d-flex justify-content-between mb-0">
<span>Kembalian</span>
<span class="fw-bold" id="changeAmount">Rp 0</span>
</div>
</div>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal" id="cancelPay">Batal</button>
<button type="button" class="btn btn-success px-4" id="confirmPay" onclick="processPayment()">Selesai & Cetak</button>
</div>
</div>
</div>
</div>
<!-- Print Section (Hidden) -->
<div id="printSection"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
let cart = [];
let selectedMemberId = null;
let selectedMemberName = '';
let selectedMemberCode = '';
let selectedMemberPoints = 0;
let appliedVoucher = null;
const subtotalEl = document.getElementById('subtotal');
const totalEl = document.getElementById('total');
const cartItemsEl = document.getElementById('cartItems');
const payBtn = document.getElementById('payBtn');
const memberSelect = document.getElementById('memberSelect');
const memberInfo = document.getElementById('memberInfo');
const memberPointsText = document.getElementById('memberPointsText');
const memberDiscountRow = document.getElementById('memberDiscountRow');
const memberSavingsEl = document.getElementById('memberSavings');
const pointsDiscountRow = document.getElementById('pointsDiscountRow');
const pointsDiscountEl = document.getElementById('pointsDiscount');
const voucherDiscountRow = document.getElementById('voucherDiscountRow');
const voucherDiscountEl = document.getElementById('voucherDiscount');
const pointsEarnedRow = document.getElementById('pointsEarnedRow');
const pointsEarnedEl = document.getElementById('pointsEarned');
const payCredit = document.getElementById('payCredit');
const payCreditLabel = document.getElementById('payCreditLabel');
const usePointsToggle = document.getElementById('usePoints');
const pointsToUseInput = document.getElementById('pointsToUse');
const pointsRedemptionInput = document.getElementById('pointsRedemptionInput');
// Product search & filter
document.getElementById('productSearch').addEventListener('input', filterProducts);
document.getElementById('categoryFilter').addEventListener('change', filterProducts);
function filterProducts() {
const search = document.getElementById('productSearch').value.toLowerCase();
const category = document.getElementById('categoryFilter').value;
const items = document.querySelectorAll('.product-item');
items.forEach(item => {
const nameMatch = item.dataset.name.includes(search);
const catMatch = !category || item.dataset.category === category;
item.classList.toggle('d-none', !(nameMatch && catMatch));
});
}
function updateMember() {
selectedMemberId = memberSelect.value;
if (selectedMemberId) {
const option = memberSelect.options[memberSelect.selectedIndex];
selectedMemberName = option.dataset.name;
selectedMemberCode = option.dataset.code;
selectedMemberPoints = parseInt(option.dataset.points);
memberPointsText.innerText = new Intl.NumberFormat('id-ID').format(selectedMemberPoints);
memberInfo.classList.remove('d-none');
memberDiscountRow.classList.remove('d-none');
pointsEarnedRow.classList.remove('d-none');
payCredit.disabled = false;
payCreditLabel.classList.remove('disabled');
} else {
selectedMemberName = '';
selectedMemberCode = '';
selectedMemberPoints = 0;
memberInfo.classList.add('d-none');
memberDiscountRow.classList.add('d-none');
pointsEarnedRow.classList.add('d-none');
usePointsToggle.checked = false;
pointsToUseInput.value = '';
payCredit.disabled = true;
payCreditLabel.classList.add('disabled');
if (payCredit.checked) document.getElementById('payCash').checked = true;
togglePaymentInputs();
}
renderCart();
}
async function applyVoucher() {
const code = document.getElementById('voucherCode').value.trim();
const statusEl = document.getElementById('voucherStatus');
if (!code) return;
try {
const response = await fetch(`api/check_voucher.php?code=${code}`);
const result = await response.json();
if (result.success) {
appliedVoucher = result.voucher;
statusEl.innerText = `Voucher berhasil digunakan: Rp ${new Intl.NumberFormat('id-ID').format(appliedVoucher.discount_amount)}`;
statusEl.className = 'small mt-1 text-success';
statusEl.classList.remove('d-none');
} else {
appliedVoucher = null;
statusEl.innerText = result.error || 'Kode voucher tidak valid';
statusEl.className = 'small mt-1 text-danger';
statusEl.classList.remove('d-none');
}
renderCart();
} catch (err) {
console.error(err);
}
}
function addToCart(product) {
const existing = cart.find(item => item.id === product.id);
if (existing) {
existing.qty++;
} else {
cart.push({...product, qty: 1});
}
renderCart();
}
function removeFromCart(id) {
cart = cart.filter(item => item.id !== id);
renderCart();
}
function updateQty(id, qty) {
const item = cart.find(i => i.id === id);
if (item) {
item.qty = Math.max(0, parseInt(qty) || 0);
if (item.qty === 0) removeFromCart(id);
else renderCart();
}
}
function renderCart() {
cartItemsEl.innerHTML = '';
let subtotal = 0;
let total = 0;
let savings = 0;
if (cart.length === 0) {
cartItemsEl.innerHTML = '<div class="text-center py-5 text-muted"><i class="bi bi-cart-x display-4"></i><p class="mt-2">Keranjang masih kosong</p></div>';
payBtn.disabled = true;
} else {
payBtn.disabled = false;
cart.forEach(item => {
const price = selectedMemberId ? item.member_price : item.selling_price;
const itemTotal = item.qty * price;
subtotal += item.qty * item.selling_price;
total += itemTotal;
savings += (item.qty * item.selling_price) - itemTotal;
const div = document.createElement('div');
div.className = `cart-item ${selectedMemberId ? 'bg-member' : ''}`;
div.innerHTML = `
<div class="d-flex align-items-start">
<div class="flex-grow-1">
<div class="fw-bold text-dark mb-1" style="font-size: 1.05rem;">${item.name}</div>
<div class="text-muted small">
Harga Satuan: Rp ${new Intl.NumberFormat('id-ID').format(price)}
</div>
</div>
<button class="btn btn-sm text-danger" onclick="removeFromCart(${item.id})"><i class="bi bi-trash"></i></button>
</div>
<div class="d-flex justify-content-between align-items-center mt-3">
<div class="d-flex align-items-center">
<span class="small text-muted me-2">Jumlah:</span>
<input type="number" class="qty-input" value="${item.qty}" onchange="updateQty(${item.id}, this.value)">
</div>
<div class="text-end">
<div class="small text-muted">Subtotal Item</div>
<div class="fw-bold text-primary" style="font-size: 1.1rem;">
Rp ${new Intl.NumberFormat('id-ID').format(itemTotal)}
</div>
</div>
</div>
`;
cartItemsEl.appendChild(div);
});
}
// Handle points redemption
let pointsDiscount = 0;
if (usePointsToggle.checked) {
pointsRedemptionInput.classList.remove('d-none');
let ptsToUse = parseInt(pointsToUseInput.value) || 0;
if (ptsToUse > selectedMemberPoints) {
ptsToUse = selectedMemberPoints;
pointsToUseInput.value = ptsToUse;
}
pointsDiscount = ptsToUse;
} else {
pointsRedemptionInput.classList.add('d-none');
}
// Handle voucher
let voucherDiscount = 0;
if (appliedVoucher) {
voucherDiscount = parseFloat(appliedVoucher.discount_amount);
voucherDiscountRow.classList.remove('d-none');
} else {
voucherDiscountRow.classList.add('d-none');
}
total = Math.max(0, total - pointsDiscount - voucherDiscount);
subtotalEl.innerText = 'Rp ' + new Intl.NumberFormat('id-ID').format(subtotal);
totalEl.innerText = 'Rp ' + new Intl.NumberFormat('id-ID').format(total);
memberSavingsEl.innerText = '- Rp ' + new Intl.NumberFormat('id-ID').format(savings);
if (pointsDiscount > 0) {
pointsDiscountRow.classList.remove('d-none');
pointsDiscountEl.innerText = '- Rp ' + new Intl.NumberFormat('id-ID').format(pointsDiscount);
} else {
pointsDiscountRow.classList.add('d-none');
}
if (voucherDiscount > 0) {
voucherDiscountEl.innerText = '- Rp ' + new Intl.NumberFormat('id-ID').format(voucherDiscount);
}
const earned = selectedMemberId ? Math.floor(total / 10000) : 0;
pointsEarnedEl.innerText = earned + ' Poin';
document.getElementById('modalTotal').innerText = 'Rp ' + new Intl.NumberFormat('id-ID').format(total);
updateChange();
}
function clearCart() {
if (confirm('Bersihkan semua item di keranjang?')) {
cart = [];
appliedVoucher = null;
document.getElementById('voucherCode').value = '';
document.getElementById('voucherStatus').classList.add('d-none');
renderCart();
}
}
// Clock
setInterval(() => {
const now = new Date();
document.getElementById('clock').innerText = now.toLocaleTimeString('id-ID');
}, 1000);
// Payment Logic
const cashReceivedInput = document.getElementById('cashReceived');
const changeAmountEl = document.getElementById('changeAmount');
const cashInputs = document.getElementById('cashInputs');
function togglePaymentInputs() {
const method = document.querySelector('input[name="payMethod"]:checked').value;
if (method === 'cash') {
cashInputs.classList.remove('d-none');
} else {
cashInputs.classList.add('d-none');
cashReceivedInput.value = '';
updateChange();
}
}
function setCash(amount) {
const currentTotal = parseInt(totalEl.innerText.replace(/[^0-9]/g, ''));
if (amount === 'exact') {
cashReceivedInput.value = currentTotal;
} else {
cashReceivedInput.value = amount;
}
updateChange();
}
function updateChange() {
const currentTotal = parseInt(totalEl.innerText.replace(/[^0-9]/g, ''));
const cash = parseFloat(cashReceivedInput.value) || 0;
const change = cash - currentTotal;
changeAmountEl.innerText = 'Rp ' + new Intl.NumberFormat('id-ID').format(Math.max(0, change));
}
cashReceivedInput.addEventListener('input', updateChange);
async function processPayment() {
const currentTotal = parseInt(totalEl.innerText.replace(/[^0-9]/g, ''));
const method = document.querySelector('input[name="payMethod"]:checked').value;
const cash = parseFloat(cashReceivedInput.value) || 0;
const change = Math.max(0, cash - currentTotal);
if (method === 'cash' && cash < currentTotal) {
alert('Uang yang diterima kurang dari total pembayaran!');
return;
}
const confirmBtn = document.getElementById('confirmPay');
confirmBtn.disabled = true;
confirmBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Memproses...';
const payload = {
member_id: selectedMemberId,
total: currentTotal,
payment_method: method,
cash_received: method === 'cash' ? cash : currentTotal,
change_amount: method === 'cash' ? change : 0,
points_redeemed: usePointsToggle.checked ? parseInt(pointsToUseInput.value) || 0 : 0,
voucher_id: appliedVoucher ? appliedVoucher.id : null,
items: cart.map(item => ({
id: item.id,
qty: item.qty,
price: selectedMemberId ? item.member_price : item.selling_price,
name: item.name
}))
};
try {
const response = await fetch('api/process_sale.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await response.json();
if (result.success) {
payload.invoice_no = result.invoice_no;
payload.date = new Date().toLocaleString('id-ID');
if (selectedMemberId) {
renderInvoice(payload);
} else {
renderReceipt(payload);
}
window.print();
location.reload();
} else {
alert('Error: ' + result.error);
confirmBtn.disabled = false;
confirmBtn.innerText = 'Selesai & Cetak';
}
} catch (error) {
console.error(error);
alert('Terjadi kesalahan jaringan.');
confirmBtn.disabled = false;
confirmBtn.innerText = 'Selesai & Cetak';
}
}
function renderReceipt(data) {
const printEl = document.getElementById('printSection');
let itemsHtml = '';
data.items.forEach(item => {
itemsHtml += `<div style="display: flex; justify-content: space-between;"><span>${item.name} x ${item.qty}</span><span>${new Intl.NumberFormat('id-ID').format(item.qty * item.price)}</span></div>`;
});
printEl.innerHTML = `
<div class="receipt-container mx-auto">
<div style="text-align: center; margin-bottom: 10px;">
<h4 style="margin: 0;"><?php echo htmlspecialchars($current_branch['name']); ?></h4>
<div style="font-size: 10px;"><?php echo htmlspecialchars($current_branch['location'] ?? ''); ?></div>
</div>
<div style="border-top: 1px dashed #000; margin: 5px 0;"></div>
<div style="font-size: 10px; margin-bottom: 10px;">
<div>No: ${data.invoice_no}</div>
<div>Tgl: ${data.date}</div>
<div>Kasir: Admin</div>
</div>
<div style="border-top: 1px dashed #000; margin: 5px 0;"></div>
${itemsHtml}
${data.points_redeemed > 0 ? `<div style="display: flex; justify-content: space-between; font-size: 10px;"><span>Tukar Poin</span><span>-Rp ${new Intl.NumberFormat('id-ID').format(data.points_redeemed)}</span></div>` : ''}
${data.voucher_id ? `<div style="display: flex; justify-content: space-between; font-size: 10px;"><span>Voucher</span><span>-Rp ${new Intl.NumberFormat('id-ID').format(appliedVoucher.discount_amount)}</span></div>` : ''}
<div style="border-top: 1px dashed #000; margin: 5px 0;"></div>
<div style="display: flex; justify-content: space-between; font-weight: bold;">
<span>TOTAL</span>
<span>Rp ${new Intl.NumberFormat('id-ID').format(data.total)}</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>Metode</span>
<span>${data.payment_method.toUpperCase()}</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>Bayar</span>
<span>Rp ${new Intl.NumberFormat('id-ID').format(data.cash_received)}</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>Kembali</span>
<span>Rp ${new Intl.NumberFormat('id-ID').format(data.change_amount)}</span>
</div>
<div style="border-top: 1px dashed #000; margin: 5px 0;"></div>
<div style="text-align: center; margin-top: 10px; font-size: 10px;">
Terima kasih atas kunjungan Anda!
</div>
</div>
`;
}
function renderInvoice(data) {
const printEl = document.getElementById('printSection');
let itemsHtml = '';
data.items.forEach((item, index) => {
itemsHtml += `<tr><td>${index + 1}</td><td>${item.name}</td><td class="text-end">${item.qty}</td><td class="text-end">Rp ${new Intl.NumberFormat('id-ID').format(item.price)}</td><td class="text-end">Rp ${new Intl.NumberFormat('id-ID').format(item.qty * item.price)}</td></tr>`;
});
printEl.innerHTML = `
<div class="invoice-container p-4">
<div class="row mb-4">
<div class="col-6">
<h3>NOTA FAKTUR</h3>
<h5 class="text-primary"><?php echo htmlspecialchars($current_branch['name']); ?></h5>
<p class="small text-muted">${data.date}</p>
</div>
<div class="col-6 text-end">
<p class="mb-1"><strong>No Invoice:</strong> ${data.invoice_no}</p>
<p class="mb-1"><strong>Pelanggan:</strong> ${selectedMemberName}</p>
<p class="mb-0"><strong>Kode:</strong> ${selectedMemberCode}</p>
</div>
</div>
<table class="table table-bordered">
<thead class="bg-light">
<tr><th>#</th><th>Nama Produk</th><th class="text-end">Qty</th><th class="text-end">Harga</th><th class="text-end">Subtotal</th></tr>
</thead>
<tbody>${itemsHtml}</tbody>
<tfoot>
${data.points_redeemed > 0 ? `<tr><td colspan="4" class="text-end">Tukar Poin</td><td class="text-end">- Rp ${new Intl.NumberFormat('id-ID').format(data.points_redeemed)}</td></tr>` : ''}
${data.voucher_id ? `<tr><td colspan="4" class="text-end">Diskon Voucher</td><td class="text-end">- Rp ${new Intl.NumberFormat('id-ID').format(appliedVoucher.discount_amount)}</td></tr>` : ''}
<tr><td colspan="4" class="text-end fw-bold">TOTAL AKHIR</td><td class="text-end fw-bold">Rp ${new Intl.NumberFormat('id-ID').format(data.total)}</td></tr>
<tr><td colspan="4" class="text-end">Metode Pembayaran</td><td class="text-end">${data.payment_method.toUpperCase()}</td></tr>
</tfoot>
</table>
</div>
`;
}
</script>
</body>
</html>