# easy-invoice/2.3.6/assets/js/invoice-save.js

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.3.6. 404 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.3.6/code/assets/js/invoice-save.js
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.3.6/raw/assets/js/invoice-save.js
- Modified: 2026-04-11T13:58:14+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.3.6/code/assets/js/invoice-save.js#L10-L20`.

```javascript
/**
 * 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 = '<svg class="ei-btn-spinner h-4 w-4 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>';

    // 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 = `<div class="field-error text-red-500 text-sm mt-1">${errorMessage}</div>`;
                            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('<span class="inline-flex items-center gap-2">' + spinnerSvg + '<span>' + eiBuilderI18n('saving', 'Saving…') + '</span></span>');
            $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('<span class="inline-flex items-center gap-2"><svg class="h-4 w-4 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" /></svg><span>' + eiBuilderI18n('update_invoice', 'Update Invoice') + '</span></span>');
                        }
                        
                        // 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); 
```
