PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.1.10
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.1.10
2.4.3 2.4.4 2.4.2 2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 All 60 releases
easy-invoice / assets / js / invoice-save.js

invoice-save.js in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.1.10, at assets/js/invoice-save.js

392 lines 16.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Invoice Save Functionality
3 *
4 * Handles saving and updating invoices in the invoice builder page.
5 */
6 (function($) {
7 'use strict';
8
9 // Check if we're on an invoice page
10 const isInvoicePage = window.location.href.includes('easy-invoice-builder') ||
11 window.location.href.includes('easy-invoice-new') ||
12 $('#invoice-form').length > 0;
13
14 if (!isInvoicePage) {
15
16 return;
17 }
18
19 // Create a global object for invoice save functionality
20 window.EasyInvoiceSave = {
21 init: function() {
22
23 this.bindEvents();
24
25 // Load invoice data if in edit mode
26 if ($('#invoice-id').val()) {
27 this.loadInvoiceData();
28 } else {
29 // No invoice ID - this is a new invoice
30 }
31 },
32
33 // Clear all error states
34 clearErrorStates: function() {
35 $('.form-input, .form-select, .form-textarea').removeClass('error');
36 $('.field-error').remove();
37 },
38
39 // Apply error states to specific fields
40 applyErrorStates: function(errors) {
41 this.clearErrorStates();
42
43 if (errors && typeof errors === 'object') {
44 Object.keys(errors).forEach(fieldName => {
45 const field = $(`[name="${fieldName}"]`);
46 if (field.length) {
47 field.addClass('error');
48
49 // Add error message below the field
50 const errorMessage = errors[fieldName];
51 if (errorMessage) {
52 const errorHtml = `<div class="field-error text-red-500 text-sm mt-1">${errorMessage}</div>`;
53 field.closest('div').append(errorHtml);
54 }
55 }
56 });
57 }
58 },
59
60 bindEvents: function() {
61
62
63 // Count save buttons to make sure it exists
64 const saveButtonCount = $('#save-invoice-btn').length;
65
66 // Only bind the click event if the button exists
67 if (saveButtonCount > 0) {
68 // Save invoice button click handler - jQuery method
69 $('#save-invoice-btn').on('click', this.handleSaveButtonClick.bind(this));
70 } else {
71 // Save button not found in the DOM
72 }
73
74 // Add form submit handler to trigger save button
75 $('#invoice-form').on('submit', function(e) {
76 e.preventDefault();
77
78 // Ensure all item totals are calculated before submission
79 $('.invoice-item').each(function() {
80 const $item = $(this);
81 if (window.EasyInvoiceBuilder && typeof window.EasyInvoiceBuilder.calculateItemTotal === 'function') {
82 window.EasyInvoiceBuilder.calculateItemTotal($item);
83 }
84 });
85
86 // Update overall totals
87 if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
88 window.EasyInvoicePayment.updateTotals();
89 }
90
91 $('#save-invoice-btn').click();
92 });
93
94 // Clear error states when user starts typing
95 $('.form-input, .form-select, .form-textarea').on('input change', function() {
96 const field = $(this);
97 if (field.hasClass('error')) {
98 field.removeClass('error');
99 field.siblings('.field-error').remove();
100 }
101 });
102 },
103
104 // Handle save button click event
105 handleSaveButtonClick: function(e) {
106 e.preventDefault();
107 this.saveInvoice();
108 },
109
110 // Function to save or update invoice
111 saveInvoice: function() {
112
113 // Show loading state
114 const $saveBtn = $('#save-invoice-btn');
115 const originalBtnText = $saveBtn.html();
116 $saveBtn.html('<i class="fas fa-spinner fa-spin mr-2"></i> Saving...');
117 $saveBtn.prop('disabled', true);
118
119 // Collect form data
120 const formData = this.collectFormData();
121
122
123
124 // Backend validation will handle all field validation
125 // No need for frontend validation since we have comprehensive backend validation
126
127 // Prepare the request data
128 const requestData = {
129 action: 'easy_invoice_save_invoice',
130 nonce: $('#invoice_nonce').val(),
131 invoice_data: formData
132 };
133
134
135
136 // Send AJAX request
137 $.ajax({
138 url: easyInvoice.ajaxUrl,
139 type: 'POST',
140 data: requestData,
141
142 success: (response) => {
143
144 // Restore button state
145 $saveBtn.html(originalBtnText);
146 $saveBtn.prop('disabled', false);
147
148 if (response.success) {
149 // Clear any previous error states
150 this.clearErrorStates();
151
152 // If this is a new invoice (no invoice_id in form), update URL without redirect
153
154 // Do not show toast here; global handler will do it
155
156 // If the button says "Save Invoice", update it to "Update Invoice"
157 if ($saveBtn.text().trim() === 'Save Invoice') {
158 $saveBtn.html('<i class="fas fa-save mr-2"></i> Update Invoice');
159 }
160
161 // Update form with any returned data if needed
162 if (response.data.invoice) {
163 // Update the invoice ID if it was newly created
164 if (response.data.invoice.id && !$('#invoice-id').val()) {
165 $('#invoice-id').val(response.data.invoice.id);
166 }
167
168 // Update the invoice status display if available
169 if (response.data.invoice.status) {
170 $('#payment-status').val(response.data.invoice.status);
171 }
172
173 // Reload client data if client_id is present
174 if (response.data.invoice.client_id) {
175 // Update the global client data if we have it
176 if (response.data.client && typeof easyInvoice !== 'undefined') {
177 easyInvoice.clientData = response.data.client;
178
179 // Update client display fields directly
180 $('#client-name').val(response.data.client.name || '');
181 $('#client-email').val(response.data.client.email || '');
182 $('#client-phone').val(response.data.client.phone || '');
183 $('#client-address').val(response.data.client.address || '');
184 }
185 }
186
187 // Update any other fields if needed
188 }
189 } else {
190 // Show error message
191 const errorMessage = response.data && response.data.message ? response.data.message : 'Failed to save invoice';
192 if (typeof EasyInvoiceToast !== 'undefined') {
193 EasyInvoiceToast.show('error', errorMessage);
194 }
195
196 // Apply error states if validation errors are provided
197 if (response.data && response.data.errors) {
198 this.applyErrorStates(response.data.errors);
199 }
200 }
201 },
202 error: (xhr, status, error) => {
203
204 // Restore button state
205 $saveBtn.html(originalBtnText);
206 $saveBtn.prop('disabled', false);
207
208 // Try to parse the response text as JSON
209 let errorMessage = 'An error occurred while saving the invoice';
210 try {
211 const jsonResponse = JSON.parse(xhr.responseText);
212 if (jsonResponse && jsonResponse.data && jsonResponse.data.message) {
213 errorMessage = jsonResponse.data.message;
214 }
215 } catch (e) {
216 // Could not parse error response as JSON
217 }
218
219 // Show error message
220 if (typeof EasyInvoiceToast !== 'undefined') {
221 EasyInvoiceToast.show('error', errorMessage);
222 }
223 }
224 });
225 },
226
227 // Function to collect form data
228 collectFormData: function() {
229 // Get all form data using jQuery's serializeArray
230 const formArray = $('#invoice-form').serializeArray();
231
232 const data = {};
233
234 // Convert form array to object - exclude individual items fields to prevent duplication
235 $.each(formArray, function(i, field) {
236 // Skip individual items fields (items[0][title], items[0][description], etc.)
237 // These will be collected separately
238 if (!field.name.match(/^items\[\d+\]\[/)) {
239 data[field.name] = field.value;
240 }
241 });
242
243 // Collect items data separately
244 const items = [];
245 const itemRows = $('.invoice-item');
246
247 itemRows.each(function(index) {
248 const itemData = {};
249 const itemFields = $(this).find('input, select, textarea');
250
251 itemFields.each(function() {
252 const fieldName = $(this).attr('name');
253 if (fieldName) {
254 // Extract the field name from items[index][field_name] format
255 const match = fieldName.match(/items\[(\d+)\]\[([^\]]+)\]/);
256 if (match) {
257 const fieldKey = match[2];
258 let fieldValue = $(this).val();
259
260 // Handle checkbox fields
261 if ($(this).attr('type') === 'checkbox') {
262 fieldValue = $(this).is(':checked') ? '1' : '0';
263 }
264
265 itemData[fieldKey] = fieldValue;
266 }
267 }
268 });
269
270
271 // Only add item if it has at least a title or description
272 if (itemData.title || itemData.description) {
273 items.push(itemData);
274 }
275 });
276
277 // Add items to form data
278 data.items = items;
279
280 return data;
281 },
282
283 // Function to initialize page for an existing invoice
284 loadInvoiceData: function() {
285 // Check if we have an invoice ID
286 var invoiceId = parseInt(easyInvoice.invoiceId);
287
288 // If we have an invoice ID, load its data
289 if (invoiceId > 0) {
290
291 // No need to make an AJAX call - data is already loaded in PHP and passed via wp_localize_script
292
293 // Initialize client data if available
294 if ($('#customer-name').val()) {
295 }
296
297 // Update the UI/preview
298 this.updatePreview();
299 } else {
300 // No invoice ID found
301 }
302 },
303
304 // Function to show notification
305 showNotification: function(type, message) {
306 // Deprecated: Use EasyInvoiceToast instead
307 },
308
309 // Update invoice preview
310 updatePreview: function() {
311 // Update invoice details
312 $('#preview-invoice-title').text($('#invoice-title').val() || 'Invoice Title');
313 $('#preview-invoice-description').text($('#invoice-description').val() || 'Invoice description will appear here.');
314 $('#preview-invoice-number').text($('#invoice-number').val() || '-');
315 $('#preview-invoice-date').text($('#invoice-date').val() || '-');
316 $('#preview-due-date').text($('#due-date').val() || '-');
317
318 // Update client details
319 $('#preview-customer-name').text($('#customer-name').val() || 'Client Name');
320 $('#preview-customer-email').text($('#customer-email').val() || '[email protected]');
321
322 // Update notes & terms
323 $('#preview-notes').text($('#notes').val() || 'Thank you for your business!');
324 $('#preview-terms').text($('#terms').val() || 'Payment is due within 30 days.');
325
326 // Update tax rate display
327 $('#preview-tax-rate').text($('#tax-rate').val() || '0');
328
329 // Update template
330 const selectedTemplate = $('input[name="invoice_template"]:checked').val() || 'standard';
331 $('#preview-live').removeClass('template-standard template-professional template-minimal')
332 .addClass('template-' + selectedTemplate);
333
334 // If payment manager is available, let it update the totals
335 if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
336 window.EasyInvoicePayment.updateTotals();
337 }
338 },
339
340 // Reload client data after save
341 reloadClientData: function(clientId) {
342 if (!clientId) {
343 return;
344 }
345
346 // Update the client ID field
347 $('#client-id').val(clientId);
348
349 // Update the select dropdown
350 $('#select-client').val(clientId);
351
352 // Check if we have client data already loaded from PHP
353 if (typeof easyInvoice !== 'undefined' && easyInvoice.clientData && easyInvoice.clientData.id == clientId) {
354 const client = easyInvoice.clientData;
355
356 // Update client display fields
357 const clientName = client.name || 'N/A';
358
359 $('#selected-client-name').text(clientName);
360 $('#selected-client-email').text(client.email || 'N/A');
361 $('#display-client-name').text(clientName);
362 $('#display-client-company').text(client.company || 'N/A');
363 $('#display-client-email').text(client.email || 'N/A');
364 $('#display-client-phone').text(client.phone || 'N/A');
365 $('#display-client-website').text(client.website || 'N/A');
366 $('#display-client-address').text(client.address || 'N/A');
367
368 // Show client info sections
369 $('#selected-client-display').show().removeClass('hidden');
370 $('#client-info-display').show().removeClass('hidden');
371 $('#no-client-message').hide().addClass('hidden');
372
373 // Update the edit client button URL
374 $('#edit-selected-client').attr('href', easyInvoice.adminUrl + 'admin.php?page=easy-invoice-client-edit&client_id=' + clientId);
375
376 // Update search input
377 $('#client-search-input').val(clientName);
378 } else {
379 // If client data is not available from PHP, we need to reload the page
380 // to get the updated client data from the server
381
382 window.location.reload();
383 }
384 }
385 };
386
387 // Initialize invoice save when document is ready
388 $(document).ready(function() {
389 window.EasyInvoiceSave.init();
390 });
391
392 })(jQuery);