Edit file File name : invoice-details.php Content :<?php // No session check needed since login is removed require_once '_helpers.php'; $invoice_id = $_GET['id'] ?? null; if (!$invoice_id) { http_response_code(400); die("Error: Invoice ID is required."); } $conn = get_db_connection(); $business = $conn->query("SELECT * FROM business_settings LIMIT 1")->fetch_assoc(); // This function is now updated to fetch from the new payments table function get_invoice_details_with_payments($conn, $invoice_id) { $invoice_data = []; $stmt = $conn->prepare("SELECT i.*, c.name as customer_name, c.email as customer_email, c.address as customer_address, c.phone as customer_phone, c.id as customer_id FROM invoices i JOIN customers c ON i.customer_id = c.id WHERE i.id = ?"); if (!$stmt) return null; $stmt->bind_param("s", $invoice_id); $stmt->execute(); $invoices = fetch_all_assoc($stmt); $stmt->close(); if (empty($invoices)) { return null; } $invoice_data['invoice'] = $invoices[0]; // Fetch from the new payments table $stmt = $conn->prepare("SELECT * FROM payments WHERE invoice_id = ? ORDER BY payment_date DESC, payments.id DESC"); $stmt->bind_param("s", $invoice_id); $stmt->execute(); $invoice_data['payments'] = fetch_all_assoc($stmt); $stmt->close(); // Fetch invoice items separately $stmt_items = $conn->prepare("SELECT * FROM invoice_items WHERE invoice_id = ?"); $stmt_items->bind_param("s", $invoice_id); $stmt_items->execute(); $invoice_data['items'] = fetch_all_assoc($stmt_items); $stmt_items->close(); return $invoice_data; } $invoice_data = get_invoice_details_with_payments($conn, $invoice_id); $conn->close(); if (!$invoice_data) { http_response_code(404); die("Invoice not found for ID: " . htmlspecialchars($invoice_id)); } $invoice = $invoice_data['invoice']; $invoice_items = $invoice_data['items']; $payment_history = $invoice_data['payments']; $total = (float)$invoice['total']; $amount_paid = (float)($invoice['amount_paid'] ?? 0); $amount_due = $total - $amount_paid; $is_paid = $invoice['status'] === 'Paid'; $status_classes = [ 'Paid' => 'bg-green-100 text-green-800', 'Due' => 'bg-yellow-100 text-yellow-800', 'Overdue' => 'bg-red-100 text-red-800', 'Draft' => 'bg-slate-100 text-slate-800', 'Partially Paid' => 'bg-blue-100 text-blue-800' ]; $status_class = $status_classes[$invoice['status']] ?? 'bg-slate-100 text-slate-800'; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Invoice #<?php echo htmlspecialchars(preg_replace('/[^0-9]/', '', $invoice['id'])); ?> - <?php echo htmlspecialchars($business['name']); ?></title> <script src="https://cdn.tailwindcss.com"></script> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Inter', sans-serif; } :root { --primary: <?php echo htmlspecialchars($business['primary_color'] ?? '217 91% 60%'); ?>; } .bg-primary { background-color: hsl(var(--primary)); } .text-primary { color: hsl(var(--primary)); } .border-primary { border-color: hsl(var(--primary)); } @media print { body * { visibility: hidden; } #print-area, #print-area * { visibility: visible; } #print-area { position: absolute; left: 0; top: 0; width: 100%; } .no-print { display: none; } } </style> </head> <body class="bg-slate-50"> <div class="flex h-screen"> <?php include '_sidebar.php'; ?> <div class="flex-1 flex flex-col overflow-hidden lg:ml-64"> <?php include '_header.php'; ?> <main class="flex-1 overflow-x-hidden overflow-y-auto bg-slate-50 p-4 sm:p-6 lg:p-8"> <div class="flex flex-col gap-8"> <!-- Header and Timeline remain the same --> <div class="no-print flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4"> <div class="flex flex-col gap-2"> <h1 class="text-2xl md:text-3xl font-bold tracking-tight">Invoice #<?php echo htmlspecialchars(preg_replace('/[^0-9]/', '', $invoice['id'])); ?></h1> <div class="flex items-center gap-4 text-sm text-slate-500 flex-wrap"> <span class="px-2 py-0.5 text-xs font-semibold rounded-full <?php echo $status_class; ?>"><?php echo htmlspecialchars($invoice['status']); ?></span> <span class="hidden sm:inline">|</span> <span>Customer</span> <a href="customer-details.php?id=<?php echo htmlspecialchars($invoice['customer_id']); ?>" class="font-semibold text-primary hover:underline"><?php echo htmlspecialchars($invoice['customer_name']); ?></a> </div> </div> <div class="flex items-center gap-2 self-end sm:self-auto"> <button class="px-4 py-2 border rounded-full text-sm font-medium hover:bg-slate-100">More actions</button> <a href="invoice-create.php" class="px-4 py-2 bg-primary text-white rounded-full text-sm font-semibold">Create another invoice</a> </div> </div> <div class="no-print flex flex-col"> <div class="flex gap-4"> <div class="relative flex flex-col items-center"><div class="absolute top-5 left-1/2 -translate-x-1/2 w-0.5 h-full bg-slate-200"></div><div class="relative z-10 flex items-center justify-center size-10 rounded-full border-2 border-primary bg-white text-primary"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg></div></div> <div class="flex-grow pb-8"><div class="bg-white border rounded-lg p-4"><div class="flex justify-between items-center"><h3 class="font-semibold">Create invoice</h3><a href="invoice-edit.php?id=<?php echo htmlspecialchars($invoice['id']); ?>" class="text-sm px-3 py-1.5 border rounded-full hover:bg-slate-50">Edit invoice</a></div><p class="text-sm text-slate-500">Created: on <?php echo date('M d, Y, g:i A', strtotime($invoice['invoice_date'])); ?></p></div></div> </div> <div class="flex gap-4"> <div class="relative flex flex-col items-center"><div class="absolute top-5 left-1/2 -translate-x-1/2 w-0.5 h-full bg-slate-200"></div><div class="relative z-10 flex items-center justify-center size-10 rounded-full border-2 <?php echo ($invoice['status'] != 'Draft') ? 'border-primary text-primary' : 'border-slate-200 text-slate-400'; ?> bg-white"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M22 2 11 13"/><path d="m22 2-7 20-4-9-9-4Z"/></svg></div></div> <div class="flex-grow pb-8"><div class="<?php echo ($invoice['status'] == 'Draft') ? 'bg-slate-50/50' : 'bg-white'; ?> border rounded-lg p-4"><div class="flex justify-between items-center"><h3 class="font-semibold">Send invoice</h3><?php if ($invoice['status'] == 'Draft'): ?><div class="flex gap-2"><button class="text-sm px-3 py-1.5 border rounded-full bg-white hover:bg-slate-50">Mark as sent</button><button class="text-sm px-3 py-1.5 border rounded-full bg-primary text-white hover:bg-opacity-90">Send invoice</button></div><?php endif; ?></div><p class="text-sm text-slate-500"><?php echo ($invoice['status'] != 'Draft' ? 'Invoice has been sent.' : 'Invoice has not been sent yet.'); ?></p></div></div> </div> <div class="flex gap-4"> <div class="relative flex flex-col items-center"><div class="relative z-10 flex items-center justify-center size-10 rounded-full border-2 <?php echo ($is_paid || $invoice['status'] === 'Partially Paid') ? 'border-primary text-primary' : 'border-slate-200 text-slate-400'; ?> bg-white"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg></div></div> <div id="payment-timeline-item" class="flex-grow pb-8"><div class="<?php echo ($is_paid || $invoice['status'] === 'Partially Paid') ? 'bg-white' : 'bg-slate-50/50'; ?> border rounded-lg p-4"><div class="flex justify-between items-center"><h3 class="font-semibold">Manage payments</h3><?php if (!$is_paid): ?><button id="record-payment-btn" class="text-sm px-3 py-1.5 border rounded-full bg-primary text-white hover:bg-opacity-90">Record a payment</button><?php endif; ?></div><div class="text-sm text-slate-600 mt-2"><span class="font-semibold text-slate-800">Amount due:</span> <?php echo format_currency($amount_due); ?> • <span class="font-semibold text-slate-800">Status:</span> <?php echo $invoice['status']; ?> • <a href="#" class="text-primary hover:underline">Send a reminder</a></div><?php if (!empty($payment_history)): ?><hr class="my-3"><div class="space-y-4"><p class="font-semibold text-sm">Payments received:</p><?php foreach ($payment_history as $index => $payment): ?><div class="text-sm p-2 bg-slate-50 rounded-md"><p><?php echo date('F j, Y', strtotime($payment['payment_date'])); ?> - A payment for <span class="font-semibold text-slate-800"><?php echo format_currency($payment['amount']); ?></span> was made using a <?php echo strtolower(htmlspecialchars($payment['method'] ?? 'payment')); ?>.</p><div class="flex items-center gap-0 sm:gap-2 mt-1 flex-wrap"><button class="send-receipt-btn text-primary text-xs font-medium hover:underline p-0 sm:p-1">Send a receipt</button><span class="hidden sm:inline">·</span><button class="edit-payment-btn text-primary text-xs font-medium hover:underline p-0 sm:p-1" data-payment-id="<?php echo htmlspecialchars($payment['id']); ?>">Edit payment</button><span class="hidden sm:inline">·</span><button class="remove-payment-btn text-red-600 text-xs font-medium hover:underline p-0 sm:p-1" data-payment-id="<?php echo htmlspecialchars($payment['id']); ?>" data-payment-amount="<?php echo htmlspecialchars($payment['amount']); ?>" data-payment-method="<?php echo htmlspecialchars($payment['method']); ?>">Remove payment</button></div></div><?php endforeach; ?></div><?php endif; ?></div></div> </div> </div> <!-- Invoice Preview --> <div id="print-area" class="bg-white p-8 rounded-lg shadow-sm border"> <header class="flex justify-between items-start mb-12"> <div><?php if (!empty($business['logo'])): ?><img src="<?php echo htmlspecialchars($business['logo']); ?>" alt="Logo" class="h-12 w-auto"><?php else: ?><div class="w-48 h-16 bg-slate-100 flex items-center justify-center rounded text-slate-400">Your Logo</div><?php endif; ?></div> <div class="text-right"><h2 class="text-4xl font-bold text-slate-800">INVOICE</h2><p class="font-semibold"><?php echo htmlspecialchars($business['name']); ?></p><p class="text-sm text-slate-500"><?php echo htmlspecialchars($business['address'] ?? '123 Main St'); ?></p></div> </header> <section class="flex justify-between items-start mb-12"> <div><p class="text-sm text-slate-500">Bill to</p><p class="font-bold text-slate-800"><?php echo htmlspecialchars($invoice['customer_name']); ?></p><p class="text-sm text-slate-500"><?php echo htmlspecialchars($invoice['customer_address']); ?></p></div> <div class="text-right"><div class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm"><span class="font-semibold">Invoice Number:</span><span><?php echo htmlspecialchars(preg_replace('/[^0-9]/', '', $invoice['id'])); ?></span><span class="font-semibold">Invoice Date:</span><span><?php echo date('F d, Y', strtotime($invoice['invoice_date'])); ?></span><span class="font-semibold">Payment Due:</span><span><?php echo date('F d, Y', strtotime($invoice['due_date'])); ?></span></div><div class="bg-slate-100 p-2 rounded-md mt-4 grid grid-cols-2"><span class="font-semibold">Amount Due (BZD):</span><span class="font-bold"><?php echo format_currency($amount_due); ?></span></div></div> </section> <section> <table class="w-full text-left"> <thead class="bg-primary text-white"><tr><th class="p-3 font-semibold">Items</th><th class="p-3 text-center font-semibold">Quantity</th><th class="p-3 text-right font-semibold">Price</th><th class="p-3 text-right font-semibold">Amount</th></tr></thead> <tbody><?php foreach($invoice_items as $item): ?> <tr class="border-b"><td class="p-3 font-medium"><?php echo htmlspecialchars($item['description']); ?></td><td class="p-3 text-center"><?php echo htmlspecialchars($item['quantity']); ?></td><td class="p-3 text-right"><?php echo format_currency($item['price']); ?></td><td class="p-3 text-right"><?php echo format_currency($item['quantity'] * $item['price']); ?></td></tr> <?php endforeach; ?></tbody> </table> </section> <section class="flex justify-end mt-8"> <div class="w-full sm:w-80 space-y-2 text-sm"><div class="flex justify-between"><span class="text-slate-500">Subtotal:</span><span><?php echo format_currency($invoice['subtotal']); ?></span></div><div class="flex justify-between"><span class="text-slate-500">Discount:</span><span>-<?php echo format_currency($invoice['discount']); ?></span></div><div class="flex justify-between"><span class="text-slate-500">Tax (<?php echo htmlspecialchars($invoice['tax']); ?>%):</span><span><?php echo format_currency((float)$invoice['subtotal'] * ((float)$invoice['tax']/100)); ?></span></div><hr/><div class="flex justify-between font-bold text-lg"><span class="text-slate-800">Total:</span><span><?php echo format_currency($total); ?></span></div><?php if ($amount_paid > 0): ?><div class="flex justify-between text-green-600"><span class="font-semibold">Amount Paid:</span><span>-<?php echo format_currency($amount_paid); ?></span></div><hr/><?php endif; ?><div class="flex justify-between font-bold text-lg"><span class="text-slate-800">Amount Due (BZD):</span><span><?php echo format_currency($amount_due); ?></span></div></div> </section> </div> </div> </main> </div> </div> <!-- Modals Container --> <div id="modal-container"></div> <!-- Static Modals --> <div id="record-payment-modal-template" class="hidden"> <div class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4"> <div class="bg-white rounded-lg shadow-xl w-full max-w-lg overflow-hidden"> <form id="payment-form"> <input type="hidden" name="action" value="record_payment"> <input type="hidden" name="invoice_id" value="<?php echo htmlspecialchars($invoice['id']); ?>"> <div class="p-6"> <h3 class="font-bold text-lg">Record a payment for this invoice</h3> <div class="mt-6 space-y-4"> <div class="grid grid-cols-3 items-center gap-4"> <label for="payment_date" class="text-right font-medium text-sm">Date</label> <div class="col-span-2 relative"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/></svg><input type="date" id="payment_date" name="payment_date" value="<?php echo date('Y-m-d'); ?>" class="pl-10 w-full p-2 border rounded-md"></div> </div> <div class="grid grid-cols-3 items-start gap-4"> <label for="amount" class="text-right font-medium text-sm pt-2">Amount</label> <div class="col-span-2"> <div class="relative"><span class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500">BZ$</span><input type="number" id="amount" name="amount" value="<?php echo number_format($amount_due, 2, '.', ''); ?>" step="0.01" required class="pl-10 w-full p-2 border rounded-md"></div> <p id="amount-message" class="text-xs text-green-600 mt-1">Invoice will be fully paid</p> </div> </div> <div class="grid grid-cols-3 items-center gap-4"> <label for="method" class="text-right font-medium text-sm">Method</label> <select id="method" name="method" class="col-span-2 w-full p-2 border rounded-md"><option value="">Select a payment method...</option><option>Cash</option><option>Check</option><option>Credit Card</option><option>Bank Transfer</option><option>Other</option></select> </div> <div class="grid grid-cols-3 items-center gap-4"> <label for="account" class="text-right font-medium text-sm">Account</label> <select id="account" name="account" class="col-span-2 w-full p-2 border rounded-md"><option value="">Select a payment account...</option><option>Cash on Hand (BZD)</option><option>Atlantic Bank (BZD)</option><option>Belize Bank (BZD)</option><option>Heritage Bank (BZD)</option><option>National Bank of Belize (BZD)</option></select> </div> <div class="grid grid-cols-3 items-start gap-4"> <label for="notes" class="text-right font-medium text-sm pt-2">Memo / notes</label> <textarea id="notes" name="notes" rows="2" class="col-span-2 w-full p-2 border rounded-md"></textarea> </div> </div> </div> <div class="px-6 py-4 bg-slate-50 flex justify-end gap-2 rounded-b-lg"> <button type="button" class="modal-cancel-btn px-4 py-2 rounded-full border font-semibold hover:bg-slate-100">Cancel</button> <button type="submit" class="px-4 py-2 rounded-full bg-primary text-white font-semibold hover:bg-opacity-90">Submit</button> </div> </form> </div> </div> </div> <div id="remove-payment-modal-template" class="hidden"> <div class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4"> <div class="bg-white rounded-lg shadow-xl w-full max-w-md overflow-hidden"> <div class="bg-red-600 text-white p-4 flex justify-between items-center"><h3 class="font-semibold text-lg">Remove invoice payment</h3><button class="modal-close-btn w-7 h-7 rounded-full hover:bg-black/20 flex items-center justify-center"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></button></div> <div class="p-6 text-center"><p id="remove-payment-details" class="font-semibold"></p><p class="text-slate-500 mt-1">Are you sure you want to remove this invoice payment?</p></div> <div class="p-6 pt-0 flex justify-center gap-2"><button class="modal-cancel-btn px-6 py-2 rounded-full border font-semibold hover:bg-slate-100">Cancel</button><button id="confirm-remove-btn" class="px-6 py-2 rounded-full bg-red-600 text-white font-semibold hover:bg-red-700">Remove payment</button></div> </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { let activeModal = null; const handleApiAction = (formData, successCallback) => { fetch('api.php', { method: 'POST', body: formData }) .then(res => res.json()) .then(data => { if (data.success) { alert(data.data.message); if (successCallback) successCallback(data.data); else window.location.reload(); } else { alert('Error: ' + data.message); } }).catch(err => { console.error(err); alert('An error occurred.'); }); }; const openModal = (modalTemplateId) => { const template = document.getElementById(modalTemplateId); if (!template) return null; const clone = template.cloneNode(true); clone.id = 'active-modal'; clone.classList.remove('hidden'); document.getElementById('modal-container').appendChild(clone); activeModal = clone; const closeModal = () => { if (activeModal) { document.getElementById('modal-container').innerHTML = ''; activeModal = null; } }; clone.querySelectorAll('.modal-cancel-btn, .modal-close-btn').forEach(btn => btn.onclick = closeModal); clone.querySelector('.fixed').onclick = (e) => { if (e.target === e.currentTarget) closeModal(); }; return { modal: clone, closeModal }; }; const removePaymentHandler = (e) => { const removeBtn = e.target.closest('.remove-payment-btn'); if (!removeBtn) return; const paymentId = removeBtn.dataset.paymentId; const amount = removeBtn.dataset.paymentAmount; const method = removeBtn.dataset.paymentMethod; const { modal, closeModal } = openModal('remove-payment-modal-template'); if (!modal) return; modal.querySelector('#remove-payment-details').textContent = `Payment for ${format_currency(amount)} using a ${method.toLowerCase()}.`; const confirmBtn = modal.querySelector('#confirm-remove-btn'); confirmBtn.onclick = () => { const formData = new FormData(); formData.append('action', 'remove_payment'); formData.append('payment_id', paymentId); formData.append('invoice_id', '<?php echo $invoice['id']; ?>'); handleApiAction(formData); }; }; const recordPaymentHandler = () => { const { modal, closeModal } = openModal('record-payment-modal-template'); if (!modal) return; const amountInput = modal.querySelector('#amount'); const amountMessage = modal.querySelector('#amount-message'); const amountDue = <?php echo $amount_due; ?>; const updateAmountMessage = () => { const currentAmount = parseFloat(amountInput.value) || 0; if (currentAmount >= amountDue) { amountMessage.textContent = 'Invoice will be fully paid'; amountMessage.classList.add('text-green-600'); amountMessage.classList.remove('text-slate-500'); } else { amountMessage.textContent = `${format_currency(amountDue - currentAmount)} remaining`; amountMessage.classList.add('text-slate-500'); amountMessage.classList.remove('text-green-600'); } }; amountInput.addEventListener('input', updateAmountMessage); updateAmountMessage(); modal.querySelector('#payment-form').onsubmit = (e) => { e.preventDefault(); handleApiAction(new FormData(e.target)); }; }; document.getElementById('payment-timeline-item').addEventListener('click', removePaymentHandler); document.getElementById('record-payment-btn')?.addEventListener('click', recordPaymentHandler); const format_currency = (amount) => 'BZ$' + parseFloat(amount).toFixed(2); }); </script> </body> </html> Save