''' // Main javascript file for Opulent POS document.addEventListener('DOMContentLoaded', () => { const page = document.body.dataset.page; // --- Logic for Cashier Checkout Page --- if (page === 'cashier_checkout') { // --- Element Selectors --- const barcodeInput = document.getElementById('barcode-scanner-input'); const productSearchInput = document.getElementById('product-search'); const productGrid = document.getElementById('product-grid'); const productGridPlaceholder = document.getElementById('product-grid-placeholder'); const cartItemsContainer = document.getElementById('cart-items'); const cartPlaceholder = document.getElementById('cart-placeholder'); const cartItemCount = document.getElementById('cart-item-count'); const cartSubtotal = document.getElementById('cart-subtotal'); const cartTax = document.getElementById('cart-tax'); const cartTotal = document.getElementById('cart-total'); const completeSaleBtn = document.getElementById('complete-sale-btn'); const cancelSaleBtn = document.getElementById('cancel-sale-btn'); const printLastInvoiceBtn = document.getElementById('print-last-invoice-btn'); // --- State Management --- let cart = JSON.parse(localStorage.getItem('cart')) || {}; // --- Utility Functions --- const debounce = (func, delay) => { let timeout; return function(...args) { const context = this; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), delay); }; }; const formatCurrency = (amount) => `PKR ${parseFloat(amount).toFixed(2)}`; // --- API Communication --- const searchProducts = async (query) => { if (query.length < 2) { productGrid.innerHTML = ''; productGridPlaceholder.style.display = 'block'; return; } try { const response = await fetch(`api/search_products.php?q=${encodeURIComponent(query)}`); if (!response.ok) throw new Error('Network response was not ok'); const products = await response.json(); renderProductGrid(products); } catch (error) { console.error('Error fetching products:', error); productGrid.innerHTML = '

Could not fetch products.

'; } }; const findProductByBarcode = async (barcode) => { try { const response = await fetch(`api/search_products.php?q=${encodeURIComponent(barcode)}&exact=true`); if (!response.ok) throw new Error('Network response was not ok'); const products = await response.json(); if (products.length > 0) { addToCart(products[0]); return true; } return false; } catch (error) { console.error('Error fetching product by barcode:', error); return false; } }; // --- Rendering Functions --- const renderProductGrid = (products) => { productGrid.innerHTML = ''; if (products.length === 0) { productGridPlaceholder.innerHTML = '

No products found.

'; productGridPlaceholder.style.display = 'block'; return; } productGridPlaceholder.style.display = 'none'; products.forEach(product => { const productCard = document.createElement('div'); productCard.className = 'col'; productCard.innerHTML = `
${product.name}

${formatCurrency(product.price)}

`; productGrid.appendChild(productCard); }); }; const renderCart = () => { cartItemsContainer.innerHTML = ''; let subtotal = 0; let itemCount = 0; if (Object.keys(cart).length === 0) { cartItemsContainer.appendChild(cartPlaceholder); } else { const table = document.createElement('table'); table.className = 'table table-sm'; table.innerHTML = ` Item Qty Price `; const tbody = table.querySelector('tbody'); for (const productId in cart) { const item = cart[productId]; subtotal += item.price * item.quantity; itemCount += item.quantity; const row = document.createElement('tr'); row.innerHTML = `
${item.name}
${formatCurrency(item.price)}
${formatCurrency(item.price * item.quantity)} `; tbody.appendChild(row); } cartItemsContainer.appendChild(table); } const tax = subtotal * 0; const total = subtotal + tax; cartSubtotal.textContent = formatCurrency(subtotal); cartTax.textContent = formatCurrency(tax); cartTotal.textContent = formatCurrency(total); cartItemCount.textContent = itemCount; completeSaleBtn.disabled = itemCount === 0; }; // --- Cart Logic --- const saveCart = () => localStorage.setItem('cart', JSON.stringify(cart)); const addToCart = (product) => { const id = product.id; if (cart[id]) { cart[id].quantity++; } else { cart[id] = { id: id, name: product.name, price: parseFloat(product.price), quantity: 1, barcode: product.barcode }; } saveCart(); renderCart(); }; const updateCartQuantity = (productId, change) => { if (cart[productId]) { cart[productId].quantity += change; if (cart[productId].quantity <= 0) delete cart[productId]; saveCart(); renderCart(); } }; const removeFromCart = (productId) => { if (cart[productId]) { delete cart[productId]; saveCart(); renderCart(); } }; const clearCart = () => { cart = {}; saveCart(); renderCart(); }; // --- Sale Completion & Invoicing --- const completeSale = async () => { if (Object.keys(cart).length === 0) return alert('Cannot complete sale with an empty cart.'); if (!confirm('Are you sure you want to complete this sale?')) return; completeSaleBtn.disabled = true; completeSaleBtn.innerHTML = ' Processing...'; try { const response = await fetch('api/complete_sale.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(cart) }); const result = await response.json(); if (response.ok && result.success) { alert(`Sale Completed Successfully! Receipt Number: ${result.receipt_number}`); localStorage.setItem('lastSale', JSON.stringify({ cart: { ...cart }, ...result })); clearCart(); } else { throw new Error(result.error || 'An unknown error occurred.'); } } catch (error) { alert(`Failed to complete sale: ${error.message}`); } finally { completeSaleBtn.disabled = false; completeSaleBtn.innerHTML = 'Complete Sale'; } }; const printInvoice = () => { const lastSale = JSON.parse(localStorage.getItem('lastSale')); if (!lastSale) { alert('No last sale found to print.'); return; } // Populate invoice details document.getElementById('invoice-receipt-number').textContent = lastSale.receipt_number; document.getElementById('invoice-cashier-name').textContent = lastSale.cashier_name || 'N/A'; document.getElementById('invoice-date').textContent = new Date(lastSale.created_at).toLocaleString(); const itemsTable = document.getElementById('invoice-items-table'); itemsTable.innerHTML = ''; let subtotal = 0; let i = 0; for (const productId in lastSale.cart) { const item = lastSale.cart[productId]; const total = item.price * item.quantity; subtotal += total; const row = itemsTable.insertRow(); row.innerHTML = ` ${++i} ${item.name} ${item.quantity} ${formatCurrency(item.price)} ${formatCurrency(total)} `; } const tax = subtotal * 0; document.getElementById('invoice-subtotal').textContent = formatCurrency(subtotal); document.getElementById('invoice-tax').textContent = formatCurrency(tax); document.getElementById('invoice-total').textContent = formatCurrency(subtotal + tax); // Print logic const invoiceContent = document.getElementById('invoice-container').innerHTML; const printWindow = window.open('', '_blank', 'height=600,width=800'); printWindow.document.write('Print Invoice'); // Include bootstrap for styling printWindow.document.write(''); printWindow.document.write(''); printWindow.document.write(''); printWindow.document.write(invoiceContent); printWindow.document.write(''); printWindow.document.close(); setTimeout(() => { // Wait for content to load printWindow.print(); }, 500); }; // --- Event Listeners --- barcodeInput.addEventListener('keyup', async (e) => { if (e.key === 'Enter') { const barcode = barcodeInput.value.trim(); if (barcode) { barcodeInput.disabled = true; const found = await findProductByBarcode(barcode); if (!found) { alert(`Product with barcode "${barcode}" not found.`); } barcodeInput.value = ''; barcodeInput.disabled = false; barcodeInput.focus(); } } }); productSearchInput.addEventListener('keyup', debounce((e) => searchProducts(e.target.value.trim()), 300)); productGrid.addEventListener('click', (e) => { const card = e.target.closest('.product-card'); if (card) { addToCart({ id: card.dataset.productId, name: card.dataset.productName, price: card.dataset.productPrice, barcode: card.dataset.productBarcode }); } }); cartItemsContainer.addEventListener('click', (e) => { const quantityChangeBtn = e.target.closest('.cart-quantity-change'); const removeItemBtn = e.target.closest('.cart-remove-item'); if (quantityChangeBtn) { updateCartQuantity(quantityChangeBtn.dataset.productId, parseInt(quantityChangeBtn.dataset.change, 10)); } if (removeItemBtn) { removeFromCart(removeItemBtn.dataset.productId); } }); cancelSaleBtn.addEventListener('click', () => { if (confirm('Are you sure you want to cancel this sale and clear the cart?')) { clearCart(); } }); completeSaleBtn.addEventListener('click', completeSale); printLastInvoiceBtn.addEventListener('click', printInvoice); // --- Initial Load --- renderCart(); barcodeInput.focus(); } // --- Logic for Admin Sales Page --- if (page === 'admin_sales') { const saleDetailsModal = new bootstrap.Modal(document.getElementById('saleDetailsModal')); const saleDetailsContent = document.getElementById('saleDetailsContent'); document.body.addEventListener('click', async (e) => { const detailsButton = e.target.closest('a.btn-outline-info[href*="action=details"]'); if (detailsButton) { e.preventDefault(); const url = new URL(detailsButton.href); const saleId = url.searchParams.get('id'); saleDetailsContent.innerHTML = '

Loading...

'; saleDetailsModal.show(); try { const response = await fetch(`api/get_sale_details.php?id=${saleId}`); if (!response.ok) throw new Error('Failed to fetch details.'); const sale = await response.json(); let itemsHtml = ''; saleDetailsContent.innerHTML = `
Receipt: ${sale.receipt_number}

Cashier: ${sale.cashier_name || 'N/A'}

Date: ${new Date(sale.created_at).toLocaleString()}


Items Sold
${itemsHtml}

Total: PKR ${parseFloat(sale.total_amount).toFixed(2)}

`; } catch (error) { saleDetailsContent.innerHTML = `

Error: ${error.message}

`; } } }); } }); ''