// Main JavaScript file for the Invoice Generation Service document.addEventListener('DOMContentLoaded', function() { // Invoice items dynamic form functionality const addItemButton = document.getElementById('add-item-btn'); if (addItemButton) { addItemButton.addEventListener('click', addInvoiceItem); } // Add event listeners to any initial remove buttons const removeButtons = document.querySelectorAll('.remove-item-btn'); removeButtons.forEach(button => { button.addEventListener('click', removeInvoiceItem); }); // Calculate amounts when quantity or unit price changes const invoiceItems = document.querySelector('.invoice-items'); if (invoiceItems) { invoiceItems.addEventListener('input', function(e) { if (e.target.name && (e.target.name.includes('quantity') || e.target.name.includes('unit_price'))) { updateItemAmount(e.target); } }); } // Form validation const invoiceForm = document.getElementById('invoice-form'); if (invoiceForm) { invoiceForm.addEventListener('submit', validateInvoiceForm); } }); // Function to add a new invoice item row function addInvoiceItem(e) { e.preventDefault(); const invoiceItems = document.querySelector('.invoice-items'); const itemTemplate = document.querySelector('.invoice-item').cloneNode(true); // Clear values in the cloned template const inputs = itemTemplate.querySelectorAll('input'); inputs.forEach(input => { input.value = ''; // Update the input name with a new index const currentIndex = parseInt(input.name.match(/\d+/)[0]); input.name = input.name.replace(`[${currentIndex}]`, `[${currentIndex + 1}]`); }); // Add event listener to the remove button const removeButton = itemTemplate.querySelector('.remove-item-btn'); if (removeButton) { removeButton.addEventListener('click', removeInvoiceItem); } invoiceItems.appendChild(itemTemplate); } // Function to remove an invoice item row function removeInvoiceItem(e) { e.preventDefault(); const invoiceItems = document.querySelector('.invoice-items'); // Don't remove if it's the only item if (invoiceItems.children.length > 1) { e.target.closest('.invoice-item').remove(); } else { // Clear values if it's the last item const inputs = e.target.closest('.invoice-item').querySelectorAll('input'); inputs.forEach(input => { input.value = ''; }); } } // Function to update the amount field based on quantity and unit price function updateItemAmount(input) { const itemRow = input.closest('.invoice-item'); const quantityInput = itemRow.querySelector('input[name*="quantity"]'); const unitPriceInput = itemRow.querySelector('input[name*="unit_price"]'); const amountDisplay = itemRow.querySelector('.amount-display'); if (quantityInput && unitPriceInput && amountDisplay) { const quantity = parseFloat(quantityInput.value) || 0; const unitPrice = parseFloat(unitPriceInput.value) || 0; const amount = (quantity * unitPrice).toFixed(2); amountDisplay.textContent = amount; } } // Function to validate the invoice form before submission function validateInvoiceForm(e) { let valid = true; // Reset any previous error messages const errorMessages = document.querySelectorAll('.error-message'); errorMessages.forEach(message => message.remove()); // Validate customer name const customerName = document.getElementById('customer_name'); if (!customerName.value.trim()) { showError(customerName, 'Customer name is required'); valid = false; } // Validate due date const dueDate = document.getElementById('due_date'); if (!dueDate.value) { showError(dueDate, 'Due date is required'); valid = false; } // Validate invoice items const itemRows = document.querySelectorAll('.invoice-item'); let hasValidItems = false; itemRows.forEach(row => { const description = row.querySelector('input[name*="description"]'); const quantity = row.querySelector('input[name*="quantity"]'); const unitPrice = row.querySelector('input[name*="unit_price"]'); if (description.value.trim() && quantity.value && unitPrice.value) { hasValidItems = true; } }); if (!hasValidItems) { const invoiceItems = document.querySelector('.invoice-items'); const errorDiv = document.createElement('div'); errorDiv.className = 'error-message'; errorDiv.textContent = 'At least one complete invoice item is required'; errorDiv.style.color = 'red'; invoiceItems.parentNode.insertBefore(errorDiv, invoiceItems.nextSibling); valid = false; } if (!valid) { e.preventDefault(); } } // Helper function to show error messages function showError(input, message) { const errorDiv = document.createElement('div'); errorDiv.className = 'error-message'; errorDiv.textContent = message; errorDiv.style.color = 'red'; input.parentNode.appendChild(errorDiv); }