/** * Invoice Save Functionality * * Handles saving and updating invoices in the invoice builder page. */ (function($) { 'use strict'; function eiBuilderI18n(key, fallback) { if (typeof easyInvoice !== 'undefined' && easyInvoice.i18n && easyInvoice.i18n[key]) { return easyInvoice.i18n[key]; } return fallback || ''; } var spinnerSvg = ''; // Check if we're on an invoice page const isInvoicePage = window.location.href.includes('easy-invoice-builder') || window.location.href.includes('easy-invoice-new') || $('#invoice-form').length > 0; if (!isInvoicePage) { return; } // Create a global object for invoice save functionality window.EasyInvoiceSave = { init: function() { this.bindEvents(); // Load invoice data if in edit mode if ($('#invoice-id').val()) { this.loadInvoiceData(); } else { // No invoice ID - this is a new invoice } }, // Clear all error states clearErrorStates: function() { $('.form-input, .form-select, .form-textarea').removeClass('error'); $('.field-error').remove(); }, // Apply error states to specific fields applyErrorStates: function(errors) { this.clearErrorStates(); if (errors && typeof errors === 'object') { Object.keys(errors).forEach(fieldName => { const field = $(`[name="${fieldName}"]`); if (field.length) { field.addClass('error'); // Add error message below the field const errorMessage = errors[fieldName]; if (errorMessage) { const errorHtml = `
${errorMessage}
`; field.closest('div').append(errorHtml); } } }); } }, bindEvents: function() { // Count save buttons to make sure it exists const saveButtonCount = $('#save-invoice-btn').length; // Only bind the click event if the button exists if (saveButtonCount > 0) { // Save invoice button click handler - jQuery method $('#save-invoice-btn').on('click', this.handleSaveButtonClick.bind(this)); } else { // Save button not found in the DOM } // Add form submit handler to trigger save button $('#invoice-form').on('submit', function(e) { e.preventDefault(); // Ensure all item totals are calculated before submission $('.invoice-item').each(function() { const $item = $(this); if (window.EasyInvoiceBuilder && typeof window.EasyInvoiceBuilder.calculateItemTotal === 'function') { window.EasyInvoiceBuilder.calculateItemTotal($item); } }); // Update overall totals if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') { window.EasyInvoicePayment.updateTotals(); } $('#save-invoice-btn').click(); }); // Clear error states when user starts typing $('.form-input, .form-select, .form-textarea').on('input change', function() { const field = $(this); if (field.hasClass('error')) { field.removeClass('error'); field.siblings('.field-error').remove(); } }); }, // Handle save button click event handleSaveButtonClick: function(e) { e.preventDefault(); this.saveInvoice(); }, // Function to save or update invoice saveInvoice: function() { // Show loading state const $saveBtn = $('#save-invoice-btn'); const originalBtnText = $saveBtn.html(); $saveBtn.html('' + spinnerSvg + '' + eiBuilderI18n('saving', 'Saving…') + ''); $saveBtn.prop('disabled', true).attr('aria-busy', 'true'); // Collect form data const formData = this.collectFormData(); // Backend validation will handle all field validation // No need for frontend validation since we have comprehensive backend validation // Prepare the request data const requestData = { action: 'easy_invoice_save_invoice', nonce: $('#invoice_nonce').val(), invoice_data: formData }; // Send AJAX request $.ajax({ url: easyInvoice.ajaxUrl, type: 'POST', data: requestData, success: (response) => { // Restore button state $saveBtn.html(originalBtnText); $saveBtn.prop('disabled', false).removeAttr('aria-busy'); if (response.success) { // Clear any previous error states this.clearErrorStates(); // If this is a new invoice (no invoice_id in form), update URL without redirect // Do not show toast here; global handler will do it var invId = $('#invoice-id').val(); if (response.data.invoice && response.data.invoice.id) { $('#invoice-id').val(response.data.invoice.id); invId = response.data.invoice.id; } if (invId && String(invId) !== '0') { $saveBtn.html('' + eiBuilderI18n('update_invoice', 'Update Invoice') + ''); } // Update form with any returned data if needed if (response.data.invoice) { // Update the invoice status display if available if (response.data.invoice.status) { $('#payment-status').val(response.data.invoice.status); } // Reload client data if client_id is present if (response.data.invoice.client_id) { // Update the global client data if we have it if (response.data.client && typeof easyInvoice !== 'undefined') { easyInvoice.clientData = response.data.client; // Update client display fields directly $('#client-name').val(response.data.client.name || ''); $('#client-email').val(response.data.client.email || ''); $('#client-phone').val(response.data.client.phone || ''); $('#client-address').val(response.data.client.address || ''); } } // Update any other fields if needed } $(document).trigger('easy-invoice-saved', [response]); } else { $(document).trigger('easy-invoice-save-failed', [response]); // Show error message const errorMessage = response.data && response.data.message ? response.data.message : 'Failed to save invoice'; if (typeof EasyInvoiceToast !== 'undefined') { EasyInvoiceToast.show('error', errorMessage); } // Apply error states if validation errors are provided if (response.data && response.data.errors) { this.applyErrorStates(response.data.errors); } } }, error: (xhr, status, error) => { $(document).trigger('easy-invoice-save-failed', [xhr]); // Restore button state $saveBtn.html(originalBtnText); $saveBtn.prop('disabled', false).removeAttr('aria-busy'); // Try to parse the response text as JSON let errorMessage = 'An error occurred while saving the invoice'; try { const jsonResponse = JSON.parse(xhr.responseText); if (jsonResponse && jsonResponse.data && jsonResponse.data.message) { errorMessage = jsonResponse.data.message; } } catch (e) { // Could not parse error response as JSON } // Show error message if (typeof EasyInvoiceToast !== 'undefined') { EasyInvoiceToast.show('error', errorMessage); } } }); }, // Function to collect form data collectFormData: function() { // Get all form data using jQuery's serializeArray const formArray = $('#invoice-form').serializeArray(); const data = {}; // Convert form array to object - exclude individual items fields to prevent duplication $.each(formArray, function(i, field) { // Skip individual items fields (items[0][title], items[0][description], etc.) // These will be collected separately if (!field.name.match(/^items\[\d+\]\[/)) { data[field.name] = field.value; } }); // Collect items data separately const items = []; const itemRows = $('.invoice-item'); itemRows.each(function(index) { const itemData = {}; const itemFields = $(this).find('input, select, textarea'); itemFields.each(function() { const fieldName = $(this).attr('name'); if (fieldName) { // Extract the field name from items[index][field_name] format const match = fieldName.match(/items\[(\d+)\]\[([^\]]+)\]/); if (match) { const fieldKey = match[2]; let fieldValue = $(this).val(); // Handle checkbox fields if ($(this).attr('type') === 'checkbox') { fieldValue = $(this).is(':checked') ? '1' : '0'; } itemData[fieldKey] = fieldValue; } } }); // Only add item if it has at least a title or description if (itemData.title || itemData.description) { items.push(itemData); } }); // Add items to form data data.items = items; return data; }, // Function to initialize page for an existing invoice loadInvoiceData: function() { // Check if we have an invoice ID var invoiceId = parseInt(easyInvoice.invoiceId); // If we have an invoice ID, load its data if (invoiceId > 0) { // No need to make an AJAX call - data is already loaded in PHP and passed via wp_localize_script // Initialize client data if available if ($('#customer-name').val()) { } // Update the UI/preview this.updatePreview(); } else { // No invoice ID found } }, // Function to show notification showNotification: function(type, message) { // Deprecated: Use EasyInvoiceToast instead }, // Update invoice preview updatePreview: function() { // Update invoice details $('#preview-invoice-title').text($('#invoice-title').val() || 'Invoice Title'); $('#preview-invoice-description').text($('#invoice-description').val() || 'Invoice description will appear here.'); $('#preview-invoice-number').text($('#invoice-number').val() || '-'); $('#preview-invoice-date').text($('#invoice-date').val() || '-'); $('#preview-due-date').text($('#due-date').val() || '-'); // Update client details $('#preview-customer-name').text($('#customer-name').val() || 'Client Name'); $('#preview-customer-email').text($('#customer-email').val() || 'client@example.com'); // Update notes & terms $('#preview-notes').text($('#notes').val() || 'Thank you for your business!'); $('#preview-terms').text($('#terms').val() || 'Payment is due within 30 days.'); // Update tax rate display $('#preview-tax-rate').text($('#tax-rate').val() || '0'); // Update template const selectedTemplate = $('input[name="invoice_template"]:checked').val() || 'standard'; $('#preview-live').removeClass('template-standard template-professional template-minimal') .addClass('template-' + selectedTemplate); // If payment manager is available, let it update the totals if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') { window.EasyInvoicePayment.updateTotals(); } }, // Reload client data after save reloadClientData: function(clientId) { if (!clientId) { return; } // Update the client ID field $('#client-id').val(clientId); // Update the select dropdown $('#select-client').val(clientId); // Check if we have client data already loaded from PHP if (typeof easyInvoice !== 'undefined' && easyInvoice.clientData && easyInvoice.clientData.id == clientId) { const client = easyInvoice.clientData; // Update client display fields const clientName = client.name || 'N/A'; $('#selected-client-name').text(clientName); $('#selected-client-email').text(client.email || 'N/A'); $('#display-client-name').text(clientName); $('#display-client-company').text(client.company || 'N/A'); $('#display-client-email').text(client.email || 'N/A'); $('#display-client-phone').text(client.phone || 'N/A'); $('#display-client-website').text(client.website || 'N/A'); $('#display-client-address').text(client.address || 'N/A'); // Show client info sections $('#selected-client-display').show().removeClass('hidden'); $('#client-info-display').show().removeClass('hidden'); $('#no-client-message').hide().addClass('hidden'); // Update the edit client button URL $('#edit-selected-client').attr('href', easyInvoice.adminUrl + 'admin.php?page=easy-invoice-client-edit&client_id=' + clientId); // Update search input $('#client-search-input').val(clientName); } else { // If client data is not available from PHP, we need to reload the page // to get the updated client data from the server window.location.reload(); } } }; // Initialize invoice save when document is ready $(document).ready(function() { window.EasyInvoiceSave.init(); }); })(jQuery);