document.addEventListener('DOMContentLoaded', function () { const barcodeInput = document.getElementById('barcode-scanner-input'); const productSearchInput = document.getElementById('product-search'); const productGrid = document.getElementById('product-grid'); const cartItemsContainer = document.getElementById('cart-items'); 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'); const cartPlaceholder = document.getElementById('cart-placeholder'); const productGridPlaceholder = document.getElementById('product-grid-placeholder'); let cart = []; const TAX_RATE = 0.00; // ========================================================================= // CORE FUNCTIONS // ========================================================================= const showToast = (message, type = 'success') => { // A simple toast notification function (can be replaced with a library) const toast = document.createElement('div'); toast.className = `toast show align-items-center text-white bg-${type} border-0`; toast.innerHTML = `
${message}
`; document.body.appendChild(toast); setTimeout(() => toast.remove(), 3000); }; const searchProducts = (query, searchBy = 'name') => { if (query.length < 2) { productGrid.innerHTML = ''; if (productGridPlaceholder) productGridPlaceholder.style.display = 'block'; return; } const url = searchBy === 'barcode' ? `../api/search_products.php?barcode=${encodeURIComponent(query)}` : `../api/search_products.php?name=${encodeURIComponent(query)}`; fetch(url) .then(response => response.json()) .then(products => { if (productGridPlaceholder) productGridPlaceholder.style.display = 'none'; productGrid.innerHTML = ''; if (products.length > 0) { if (searchBy === 'barcode' && products.length === 1) { addToCart(products[0]); barcodeInput.value = ''; // Clear input after successful scan barcodeInput.focus(); } else { products.forEach(product => { const productCard = `
${product.name}

PKR ${parseFloat(product.price).toFixed(2)}

`; productGrid.innerHTML += productCard; }); } } else { if (productGridPlaceholder) productGridPlaceholder.style.display = 'block'; if (searchBy === 'barcode') { showToast('Product not found for this barcode.', 'danger'); barcodeInput.value = ''; barcodeInput.focus(); } } }) .catch(error => { console.error('Error searching products:', error); showToast('Failed to search for products.', 'danger'); }); }; const addToCart = (product) => { const existingItem = cart.find(item => item.id === product.id); if (existingItem) { existingItem.quantity++; } else { cart.push({ ...product, quantity: 1 }); } updateCartView(); showToast(`${product.name} added to cart.`, 'success'); }; const updateCartView = () => { if (cart.length === 0) { if (cartPlaceholder) cartPlaceholder.style.display = 'block'; cartItemsContainer.innerHTML = cartPlaceholder ? cartPlaceholder.outerHTML : ''; } else { if (cartPlaceholder) cartPlaceholder.style.display = 'none'; const table = ` ${cart.map(item => ` `).join('')}
Item Qty Price Actions
${item.name} PKR ${parseFloat(item.price).toFixed(2)}
`; cartItemsContainer.innerHTML = table; } updateCartTotal(); }; const updateCartTotal = () => { const subtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); const tax = subtotal * TAX_RATE; const total = subtotal + tax; cartSubtotal.textContent = `PKR ${subtotal.toFixed(2)}`; cartTax.textContent = `PKR ${tax.toFixed(2)}`; cartTotal.textContent = `PKR ${total.toFixed(2)}`; cartItemCount.textContent = cart.reduce((sum, item) => sum + item.quantity, 0); completeSaleBtn.disabled = cart.length === 0; }; const completeSale = () => { if (cart.length === 0) return; fetch('../api/complete_sale.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ cart: cart }) }) .then(response => response.json()) .then(data => { if (data.success) { showToast('Sale completed successfully!', 'success'); saveInvoiceForOffline(data.sale_details); printInvoice(data.sale_details); cart = []; updateCartView(); } else { showToast(`Sale failed: ${data.message}`, 'danger'); } }) .catch(error => { console.error('Error completing sale:', error); showToast('An error occurred while completing the sale.', 'danger'); }); }; const cancelSale = () => { if (confirm('Are you sure you want to cancel this sale?')) { cart = []; updateCartView(); showToast('Sale cancelled.', 'info'); } }; const saveInvoiceForOffline = (saleDetails) => { try { localStorage.setItem('lastInvoice', JSON.stringify(saleDetails)); } catch (e) { console.error('Could not save invoice to localStorage:', e); } }; const printInvoice = (invoiceData) => { if (!invoiceData) { showToast('No invoice data to print.', 'warning'); return; } document.getElementById('invoice-receipt-number').textContent = invoiceData.sale_id; document.getElementById('invoice-cashier-name').textContent = invoiceData.cashier_name || 'N/A'; document.getElementById('invoice-date').textContent = new Date(invoiceData.sale_date).toLocaleString(); const itemsHtml = invoiceData.items.map((item, index) => ` ${index + 1} ${item.name} ${item.quantity} PKR ${parseFloat(item.price).toFixed(2)} PKR ${(item.quantity * item.price).toFixed(2)} `).join(''); document.getElementById('invoice-items-table').innerHTML = itemsHtml; const subtotal = invoiceData.items.reduce((sum, item) => sum + (item.price * item.quantity), 0); const tax = subtotal * TAX_RATE; const total = subtotal + tax; document.getElementById('invoice-subtotal').textContent = `PKR ${subtotal.toFixed(2)}`; document.getElementById('invoice-tax').textContent = `PKR ${tax.toFixed(2)}`; document-getElementById('invoice-total').textContent = `PKR ${total.toFixed(2)}`; const invoiceHtml = document.getElementById('invoice-container').innerHTML; const printWindow = window.open('', '_blank'); printWindow.document.write('Print Invoice'); // Optional: Add bootstrap for styling the print view printWindow.document.write(''); printWindow.document.write(''); printWindow.document.write(invoiceHtml.replace('d-none', '')); // Show the invoice printWindow.document.write(''); printWindow.document.close(); setTimeout(() => { // Wait for content to load printWindow.print(); printWindow.close(); }, 500); }; const printLastInvoice = () => { try { const lastInvoice = localStorage.getItem('lastInvoice'); if (lastInvoice) { printInvoice(JSON.parse(lastInvoice)); } else { showToast('No previously saved invoice found.', 'info'); } } catch (e) { console.error('Could not retrieve or print last invoice:', e); showToast('Failed to print last invoice.', 'danger'); } }; // ========================================================================= // EVENT LISTENERS // ========================================================================= if (barcodeInput) { barcodeInput.addEventListener('change', (e) => { // 'change' is often better for scanners const barcode = e.target.value.trim(); if (barcode) { searchProducts(barcode, 'barcode'); } }); barcodeInput.focus(); // Keep focus on the barcode input } if (productSearchInput) { productSearchInput.addEventListener('keyup', (e) => { searchProducts(e.target.value.trim(), 'name'); }); } if (productGrid) { productGrid.addEventListener('click', (e) => { const card = e.target.closest('.product-card'); if (card) { const productId = card.dataset.productId; // We need to fetch the full product details to add to cart fetch(`../api/search_products.php?id=${productId}`) .then(res => res.json()) .then(product => { if(product) addToCart(product); }); } }); } if (cartItemsContainer) { cartItemsContainer.addEventListener('change', (e) => { if (e.target.classList.contains('quantity-input')) { const productId = parseInt(e.target.dataset.productId); const newQuantity = parseInt(e.target.value); const itemInCart = cart.find(item => item.id === productId); if (itemInCart) { if (newQuantity > 0) { itemInCart.quantity = newQuantity; } else { // Remove if quantity is 0 or less cart = cart.filter(item => item.id !== productId); } updateCartView(); } } }); cartItemsContainer.addEventListener('click', (e) => { if (e.target.classList.contains('remove-item-btn')) { const productId = parseInt(e.target.dataset.productId); cart = cart.filter(item => item.id !== productId); updateCartView(); } }); } if (completeSaleBtn) completeSaleBtn.addEventListener('click', completeSale); if (cancelSaleBtn) cancelSaleBtn.addEventListener('click', cancelSale); if (printLastInvoiceBtn) printLastInvoiceBtn.addEventListener('click', printLastInvoice); // Initial setup updateCartView(); });