File manager - Edit - /home2/zetasolve/speedfood.zetasolve.agency/wp-admin/billing.js.tar
Back
home2/zetasolve/crm.shayantraders.com/scripts/billing.js 0000644 00000062771 15225746660 0017476 0 ustar 00 let allSkus = []; let cart = []; document.addEventListener('DOMContentLoaded', async () => { // Set today's date document.getElementById('transactionDate').valueAsDate = new Date(); // Wait a bit to ensure authentication is properly established setTimeout(async () => { console.log('Checking authentication before loading SKUs...'); // Verify user is authenticated const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true'; const userRole = localStorage.getItem('role'); if (!isLoggedIn || !userRole) { console.error('User not properly authenticated. Redirecting to login...'); window.location.href = '../login.html'; return; } console.log('User authenticated, loading SKUs...'); // Load SKUs await loadSkus(); // Restore cart from session storage restoreCartState(); // Initialize discount input initializeDiscountInput(); }, 100); // Small delay to ensure all auth checks are complete }); // Save cart state to session storage function saveCartState() { sessionStorage.setItem('billingCart', JSON.stringify(cart)); sessionStorage.setItem('billingCustomerName', document.getElementById('customerName').value || ''); // Save discount if exists const discountInput = document.getElementById('discountPercentage'); if (discountInput) { sessionStorage.setItem('billingDiscountPercentage', discountInput.value); } // Save amount paid const amountPaidInput = document.getElementById('amountPaid'); if (amountPaidInput) { sessionStorage.setItem('billingAmountPaid', amountPaidInput.value); } } // Restore cart state from session storage function restoreCartState() { const savedCart = sessionStorage.getItem('billingCart'); if (savedCart) { cart = JSON.parse(savedCart); updateCartDisplay(); } // Restore customer name const savedCustomerName = sessionStorage.getItem('billingCustomerName'); if (savedCustomerName) { document.getElementById('customerName').value = savedCustomerName; } // Restore discount const savedDiscount = sessionStorage.getItem('billingDiscountPercentage'); if (savedDiscount !== null) { const discountInput = document.getElementById('discountPercentage'); if (discountInput) { discountInput.value = savedDiscount; updateDiscountAmount(); // Update discount display } } // Restore amount paid const savedAmountPaid = sessionStorage.getItem('billingAmountPaid'); if (savedAmountPaid !== null) { const amountPaidInput = document.getElementById('amountPaid'); if (amountPaidInput) { amountPaidInput.value = savedAmountPaid; } } } // Clear saved cart state function clearCartState() { sessionStorage.removeItem('billingCart'); sessionStorage.removeItem('billingCustomerName'); sessionStorage.removeItem('billingDiscountPercentage'); sessionStorage.removeItem('billingAmountPaid'); } async function loadSkus() { try { // Add a small delay to ensure authentication is fully established await new Promise(resolve => setTimeout(resolve, 500)); allSkus = await app.apiGet('/skus'); const skuSelect = document.getElementById('skuSelect'); if (allSkus && allSkus.length > 0) { skuSelect.innerHTML = '<option value="">Select a product...</option>' + allSkus.map(sku => ` <option value="${sku.id}" data-price="${sku.price}" data-stock="${sku.current_stock}"> ${sku.sku_code} - ${sku.product_name} ${sku.flavour} (${sku.weight}) - ${app.formatCurrency(sku.price)} [Stock: ${sku.current_stock}] </option> `).join(''); console.log(`Loaded ${allSkus.length} SKUs successfully`); } else { skuSelect.innerHTML = '<option value="">No products available</option>'; console.log('No SKUs found in database'); } } catch (error) { console.error('Error loading SKUs:', error); console.error('Full error details:', error.message, error.stack); const skuSelect = document.getElementById('skuSelect'); skuSelect.innerHTML = '<option value="">Failed to load products</option>'; app.showNotification('Failed to load products. Please check your connection and refresh the page.', 'error'); // Try alternative loading after a delay setTimeout(async () => { try { allSkus = await app.apiGet('/skus'); if (allSkus && allSkus.length > 0) { const skuSelect = document.getElementById('skuSelect'); skuSelect.innerHTML = '<option value="">Select a product...</option>' + allSkus.map(sku => ` <option value="${sku.id}" data-price="${sku.price}" data-stock="${sku.current_stock}"> ${sku.sku_code} - ${sku.product_name} ${sku.flavour} (${sku.weight}) - ${app.formatCurrency(sku.price)} [Stock: ${sku.current_stock}] </option> `).join(''); app.showNotification('Products loaded successfully!', 'success'); } } catch (retryError) { console.error('Retry also failed:', retryError); } }, 2000); } } function addItemToCart() { const skuSelect = document.getElementById('skuSelect'); const quantity = parseInt(document.getElementById('itemQuantity').value); if (!skuSelect.value) { app.showNotification('Please select a product', 'error'); return; } if (quantity < 1) { app.showNotification('Quantity must be at least 1', 'error'); return; } const skuId = parseInt(skuSelect.value); const sku = allSkus.find(s => s.id === skuId); const selectedOption = skuSelect.options[skuSelect.selectedIndex]; const availableStock = parseInt(selectedOption.dataset.stock); // Check if already in cart const existingItem = cart.find(item => item.sku_id === skuId); const totalQuantity = existingItem ? existingItem.quantity + quantity : quantity; if (totalQuantity > availableStock) { app.showNotification(`Insufficient stock! Available: ${availableStock}`, 'error'); return; } if (existingItem) { existingItem.quantity = totalQuantity; } else { cart.push({ sku_id: skuId, sku_code: sku.sku_code, product_name: sku.product_name, flavour: sku.flavour, weight: sku.weight, unit_price: sku.price, quantity: quantity }); } // Reset selection skuSelect.value = ''; document.getElementById('itemQuantity').value = 1; updateCartDisplay(); saveCartState(); // Save state after adding item } function removeItemFromCart(index) { cart.splice(index, 1); updateCartDisplay(); saveCartState(); // Save state after removing item } function updateCartDisplay() { const cartContainer = document.getElementById('cartItems'); const submitBtn = document.getElementById('submitBtn'); const previewInvoiceBtn = document.getElementById('previewInvoiceBtn'); if (cart.length === 0) { cartContainer.innerHTML = '<p class="text-muted" style="text-align: center; padding: 2rem;">No items added yet</p>'; submitBtn.disabled = true; previewInvoiceBtn.disabled = true; updateBillSummary(); return; } submitBtn.disabled = false; previewInvoiceBtn.disabled = false; cartContainer.innerHTML = ` <div style="background: var(--bg-tertiary); border-radius: var(--radius-md); padding: 1rem;"> ${cart.map((item, index) => ` <div style="display: flex; justify-content: space-between; align-items: center; padding: 0.75rem 0; ${index < cart.length - 1 ? 'border-bottom: 1px solid var(--glass-border);' : ''}"> <div style="flex: 1;"> <div style="font-weight: 600; color: var(--text-primary);">${item.sku_code}</div> <div style="font-size: 0.875rem; color: var(--text-muted);">${item.product_name} ${item.flavour} (${item.weight})</div> </div> <div style="display: flex; align-items: center; gap: 1rem;"> <div style="text-align: right;"> <div style="color: var(--text-secondary);">${item.quantity} × ${app.formatCurrency(item.unit_price)}</div> <div style="font-weight: 600; color: var(--text-primary);">${app.formatCurrency(item.quantity * item.unit_price)}</div> </div> <button type="button" class="btn btn-danger" onclick="removeItemFromCart(${index})" style="padding: 0.5rem 0.75rem; font-size: 0.75rem;"> Remove </button> </div> </div> `).join('')} </div> `; updateBillSummary(); } function updateBillSummary() { const itemCount = cart.reduce((sum, item) => sum + item.quantity, 0); const subtotal = cart.reduce((sum, item) => sum + (item.quantity * item.unit_price), 0); // Get user role to determine if discount is allowed const userRole = localStorage.getItem('role'); const discountAllowed = ['Admin', 'Manager'].includes(userRole); // Calculate discount - only manual discount allowed (no automatic discount) let discountPercentage = 0; let discountAmount = 0; let finalTotal = subtotal; // Check for manual discount const discountPercentageInput = document.getElementById('discountPercentage'); if (discountPercentageInput && discountPercentageInput.value) { if (discountAllowed) { const manualDiscount = getManualDiscount(); discountPercentage = manualDiscount.percentage; discountAmount = manualDiscount.amount; finalTotal = subtotal - discountAmount; } } document.getElementById('summaryItemCount').textContent = itemCount; document.getElementById('summarySubtotal').textContent = app.formatCurrency(subtotal); document.getElementById('summaryDiscount').textContent = app.formatCurrency(discountAmount); document.getElementById('summaryTotal').textContent = app.formatCurrency(finalTotal); } async function processSale(event) { event.preventDefault(); if (cart.length === 0) { app.showNotification('Please add items to the cart', 'error'); return; } // Get user role to determine if discount is allowed const userRole = localStorage.getItem('role'); const discountAllowed = ['Admin', 'Manager'].includes(userRole); // Calculate discount for the transaction (manual discount only, no automatic discount) const subtotal = cart.reduce((sum, item) => sum + (item.quantity * item.unit_price), 0); let discountPercentage = 0; let discountAmount = 0; // Check if manual discount is entered const discountPercentageInput = document.getElementById('discountPercentage'); if (discountPercentageInput && discountPercentageInput.value) { // Validate discount authorization if (!discountAllowed) { app.showNotification('Only Admin or Manager can apply manual discounts', 'error'); return; } const manualDiscount = getManualDiscount(); discountPercentage = manualDiscount.percentage; discountAmount = manualDiscount.amount; } // Note: Automatic discount logic (bill ≥ 2000 PKR → 10% discount) has been removed // Only manual discounts are allowed now const finalTotal = subtotal - discountAmount; // Get amount paid from the new input field const amountPaidInput = document.getElementById('amountPaid'); let amountPaid = 0; if (amountPaidInput && amountPaidInput.value) { amountPaid = parseFloat(amountPaidInput.value); if (isNaN(amountPaid) || amountPaid < 0) { app.showNotification('Please enter a valid amount paid', 'error'); return; } if (amountPaid < finalTotal) { app.showNotification('Amount paid must be equal to or greater than the total amount', 'error'); return; } } else { // If no amount is entered, default to the final total amountPaid = finalTotal; } // For cash-only flow, payment type is always 'Cash' const transactionData = { transaction_date: document.getElementById('transactionDate').value, customer_name: document.getElementById('customerName').value || 'Walk-in Customer', payment_type: 'Cash', // Fixed to Cash only items: cart, discount_percentage: discountPercentage, discount_amount: discountAmount, amount_paid: amountPaid }; try { const result = await app.apiPost('/transactions', transactionData); app.showNotification(`Sale completed! Total: ${app.formatCurrency(result.final_total || result.total_amount)}`, 'success'); // Reset form and clear saved state cart = []; document.getElementById('billingForm').reset(); document.getElementById('transactionDate').valueAsDate = new Date(); updateCartDisplay(); clearCartState(); // Clear saved state after successful sale // Reload SKUs to update stock levels await loadSkus(); } catch (error) { console.error('Error processing sale:', error); app.showNotification('Failed to process sale', 'error'); } } async function viewSalesHistory() { document.getElementById('salesHistorySection').style.display = 'block'; await loadSalesHistory(); } function closeSalesHistory() { document.getElementById('salesHistorySection').style.display = 'none'; } async function loadSalesHistory(filters = {}) { try { let url = '/transactions'; const params = new URLSearchParams(); if (filters.start_date) params.append('start_date', filters.start_date); if (filters.end_date) params.append('end_date', filters.end_date); if (params.toString()) url += '?' + params.toString(); const transactions = await app.apiGet(url); const tbody = document.getElementById('salesHistoryBody'); if (transactions.length === 0) { tbody.innerHTML = '<tr><td colspan="5" class="loading">No transactions found</td></tr>'; return; } tbody.innerHTML = transactions.map(transaction => ` <tr class="fade-in"> <td>${app.formatDate(transaction.transaction_date)}</td> <td>${transaction.customer_name}</td> <td> <div><strong>${app.formatCurrency(transaction.total_amount)}</strong></div> ${transaction.discount_amount > 0 ? `<div style="font-size: 0.8rem; color: var(--success);">-${transaction.discount_percentage}% (-${app.formatCurrency(transaction.discount_amount)})</div>` : ''} </td> <td> <button class="btn btn-secondary" onclick="viewTransactionDetails(${transaction.id})" style="padding: 0.5rem 1rem; font-size: 0.75rem;"> View Details </button> <button class="btn btn-danger" onclick="deleteTransaction(${transaction.id})" style="padding: 0.5rem 1rem; font-size: 0.75rem; margin-left: 0.5rem;"> Void </button> </td> </tr> `).join(''); } catch (error) { console.error('Error loading sales history:', error); document.getElementById('salesHistoryBody').innerHTML = '<tr><td colspan="5" class="loading">Failed to load sales history</td></tr>'; } } function filterSalesHistory() { const filters = { start_date: document.getElementById('filterStartDate').value, end_date: document.getElementById('filterEndDate').value }; loadSalesHistory(filters); } async function viewTransactionDetails(transactionId) { try { const transaction = await app.apiGet(`/transactions/${transactionId}`); const content = ` <div style="margin-bottom: 1rem;"> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem;"> <div> <strong>Date:</strong> ${app.formatDate(transaction.transaction_date)} </div> <div> <strong>Customer:</strong> ${transaction.customer_name} </div> <div> <strong>Payment:</strong> <span class="badge badge-cash">Cash</span> </div> <div> <strong>Total:</strong> <span style="color: var(--danger); font-weight: 700;">${app.formatCurrency(transaction.total_amount)}</span> </div> ${transaction.discount_amount > 0 ? ` <div> <strong>Discount:</strong> <span style="color: var(--success); font-weight: 700;">${transaction.discount_percentage}% (-${app.formatCurrency(transaction.discount_amount)})</span> </div> <div> <strong>Final Total:</strong> <span style="color: var(--danger); font-weight: 700;">${app.formatCurrency(transaction.total_amount - transaction.discount_amount)}</span> </div> ` : ''} </div> </div> <h4 style="margin: 1.5rem 0 1rem;">Items</h4> <div class="table-container"> <table> <thead> <tr> <th>SKU</th> <th>Product</th> <th>Qty</th> <th>Price</th> <th>Subtotal</th> </tr> </thead> <tbody> ${transaction.items.map(item => ` <tr> <td>${item.sku_code}</td> <td>${item.product_name} ${item.flavour} (${item.weight})</td> <td>${item.quantity}</td> <td>${app.formatCurrency(item.unit_price)}</td> <td><strong>${app.formatCurrency(item.subtotal)}</strong></td> </tr> `).join('')} </tbody> </table> </div> `; document.getElementById('transactionDetailsContent').innerHTML = content; document.getElementById('transactionDetailsModal').classList.add('active'); } catch (error) { console.error('Error loading transaction details:', error); app.showNotification('Failed to load transaction details', 'error'); } } function closeTransactionDetails() { document.getElementById('transactionDetailsModal').classList.remove('active'); } // Function to preview invoice from current cart function previewInvoiceFromCart() { if (cart.length === 0) { app.showNotification('Please add items to the cart first', 'error'); return; } // Calculate totals const subtotal = cart.reduce((sum, item) => sum + (item.quantity * item.unit_price), 0); const userRole = localStorage.getItem('role'); const discountAllowed = ['Admin', 'Manager'].includes(userRole); let discountPercentage = 0; let discountAmount = 0; let finalTotal = subtotal; // Check for manual discount const discountPercentageInput = document.getElementById('discountPercentage'); if (discountPercentageInput && discountPercentageInput.value) { if (discountAllowed) { const manualDiscount = getManualDiscount(); discountPercentage = manualDiscount.percentage; discountAmount = manualDiscount.amount; finalTotal = subtotal - discountAmount; } } // For cash-only flow, payment type is always 'Cash' const effectivePaymentType = 'Cash'; // Get amount paid from the new input field const amountPaidInput = document.getElementById('amountPaid'); let amountPaid = 0; if (amountPaidInput && amountPaidInput.value) { amountPaid = parseFloat(amountPaidInput.value); if (isNaN(amountPaid) || amountPaid < 0) { app.showNotification('Please enter a valid amount paid', 'error'); return; } } else { // If no amount is entered, default to the final total amountPaid = finalTotal; } // Create a temporary transaction-like object for invoice display const tempTransaction = { id: 'preview', transaction_date: new Date().toISOString().split('T')[0], customer_name: document.getElementById('customerName').value || 'Walk-in Customer', payment_type: effectivePaymentType, items: cart, total_amount: subtotal, discount_percentage: discountPercentage, discount_amount: discountAmount, amount_paid: amountPaid }; // Store the temporary transaction in session storage sessionStorage.setItem('tempTransactionPreview', JSON.stringify(tempTransaction)); // Save current cart state so it persists when user returns saveCartState(); // Open invoices page in a new tab/window window.open('../pages/invoices.html', '_blank'); } async function deleteTransaction(transactionId) { if (confirm('Are you sure you want to VOID this transaction? This will reverse stock changes.')) { try { await app.apiDelete(`/transactions/${transactionId}`); app.showNotification('Transaction voided successfully', 'success'); await loadSalesHistory(); // Refresh history await loadSkus(); // Refresh stock } catch (error) { console.error('Error voiding transaction:', error); app.showNotification('Failed to void transaction', 'error'); } } } // Discount functions function toggleDiscountInput() { const userRole = localStorage.getItem('role'); const discountAllowed = ['Admin', 'Manager'].includes(userRole); const discountGroup = document.getElementById('discountGroup'); if (discountAllowed) { discountGroup.style.display = 'block'; } else { discountGroup.style.display = 'none'; clearDiscount(); } } function updateDiscountAmount() { const discountPercentageInput = document.getElementById('discountPercentage'); const discountAmountDisplay = document.getElementById('discountAmountDisplay'); const subtotal = cart.reduce((sum, item) => sum + (item.quantity * item.unit_price), 0); if (!discountPercentageInput.value) { discountAmountDisplay.textContent = ''; updateBillSummary(); saveCartState(); // Save state when discount is cleared return; } const discountPercentage = parseFloat(discountPercentageInput.value); if (discountPercentage < 0 || discountPercentage > 100) { app.showNotification('Discount percentage must be between 0 and 100', 'error'); discountPercentageInput.value = ''; discountAmountDisplay.textContent = ''; updateBillSummary(); saveCartState(); // Save state when discount is cleared due to error return; } const discountAmount = subtotal * (discountPercentage / 100); discountAmountDisplay.textContent = `-${app.formatCurrency(discountAmount)}`; updateBillSummary(); saveCartState(); // Save state when discount is updated } function clearDiscount() { document.getElementById('discountPercentage').value = ''; document.getElementById('discountAmountDisplay').textContent = ''; updateBillSummary(); saveCartState(); // Save state when discount is cleared } function getManualDiscount() { const discountPercentageInput = document.getElementById('discountPercentage'); if (!discountPercentageInput.value) return { percentage: 0, amount: 0 }; const subtotal = cart.reduce((sum, item) => sum + (item.quantity * item.unit_price), 0); const discountPercentage = parseFloat(discountPercentageInput.value); const discountAmount = subtotal * (discountPercentage / 100); return { percentage: discountPercentage, amount: discountAmount }; } // Initialize discount input visibility on page load function initializeDiscountInput() { const userRole = localStorage.getItem('role'); const discountAllowed = ['Admin', 'Manager'].includes(userRole); const discountGroup = document.getElementById('discountGroup'); if (discountAllowed) { discountGroup.style.display = 'block'; } else { discountGroup.style.display = 'none'; clearDiscount(); } }
| ver. 1.4 |
Github
|
.
| PHP 8.2.31 | Generation time: 0 |
proxy
|
phpinfo
|
Settings