PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.1
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.1
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 2.1.15 2.1.16 2.1.2 All 57 releases
← All changes | includes/Forms/FormProcessor.php +25 -5 2.2.02.4.1 View file →
@@ -96,12 +96,21 @@
96 96 }
97 97 $required = $field['required'] ?? false;
98 98 $field_type = $field['type'] ?? 'text';
99 99 $raw_value = $raw_data[$field_name] ?? '';
100 -
100 +
101 + // A browser omits an unchecked checkbox from the post. Treating "absent"
102 + // as "leave it alone" meant the box could never be turned off: with tax
103 + // on globally, "Apply Tax to This Invoice" always came back checked and
104 + // saved as yes. Absent now means unchecked, the way a browser means it.
105 + if ('checkbox' === $field_type && ! array_key_exists($field_name, $raw_data)) {
106 + $raw_value = '0';
107 + }
108 +
101 109 // Check required fields
102 110 if ($required && empty($raw_value)) {
103 111 $errors[$field_name] = sprintf(
112 + /* translators: %s: field name. */
104 113 __('%s is required.', 'easy-invoice'),
105 114 $field['label'] ?? $field_name
106 115 );
107 116 continue;
@@ -108,10 +117,12 @@
108 117 }
109 118
110 119 // Always include textarea fields (like description) even when empty
111 120 // This allows users to clear these fields by submitting empty values
112 - $always_include_fields = ['description', 'notes', 'terms', 'internal_notes'];
113 - $should_always_include = in_array($field_name, $always_include_fields) || $field_type === 'textarea';
121 + // Attachments too: an emptied list must reach the save, or the
122 + // last file can never be removed.
123 + $always_include_fields = ['description', 'notes', 'terms', 'internal_notes', 'attachments'];
124 + $should_always_include = in_array($field_name, $always_include_fields) || in_array($field_type, ['textarea', 'attachments'], true);
114 125
115 126 // Skip empty non-required fields (unless they should always be included)
116 127 if (empty($raw_value) && !$required && $raw_value !== '0' && !$should_always_include) {
117 128 continue;
@@ -137,8 +148,17 @@
137 148
138 149 $processed_data[$field_name] = $processed_value;
139 150 }
140 151
152 + // A percentage discount above 100 or a negative discount is a typo, not a
153 + // deal; say so instead of saving a document whose total the model has to clamp.
154 + if (isset($processed_data['discount_type']) && 'percentage' === $processed_data['discount_type'] && (float) ($processed_data['discount_value'] ?? 0) > 100) {
155 + $errors['discount_value'] = __('A percentage discount cannot be more than 100%.', 'easy-invoice');
156 + }
157 + if (isset($processed_data['discount_value']) && '' !== $processed_data['discount_value'] && (float) $processed_data['discount_value'] < 0) {
158 + $errors['discount_value'] = __('The discount cannot be negative.', 'easy-invoice');
159 + }
160 +
141 161 // Always include payment_gateways in processed data
142 162 $processed_data['payment_gateways'] = $payment_gateways_value;
143 163
144 164 // Allow plugins to modify the processed data
@@ -250,9 +270,9 @@
250 270 // Get value from form data
251 271 $value = $form_data[$field_name] ?? null;
252 272
253 273 // Fields that should always be saved, even if empty (to allow clearing)
254 - $always_save_fields = ['description', 'notes', 'terms', 'internal_notes'];
274 + $always_save_fields = ['description', 'notes', 'terms', 'internal_notes', 'attachments'];
255 275 $should_always_save = in_array($field_name, $always_save_fields);
256 276
257 277 // Save if value exists and is not empty (or is 0), OR if it's a field that should always be saved
258 278 if (($value !== null && $value !== '') || ($should_always_save && array_key_exists($field_name, $form_data))) {
@@ -268,9 +288,9 @@
268 288 }
269 289 }
270 290
271 291 // Also process any extra fields that might not be in the configuration
272 - $always_save_fields = ['description', 'notes', 'terms', 'internal_notes'];
292 + $always_save_fields = ['description', 'notes', 'terms', 'internal_notes', 'attachments'];
273 293 foreach ($form_data as $field_name => $value) {
274 294 $should_always_save = in_array($field_name, $always_save_fields);
275 295
276 296 if (($value !== null && $value !== '') || ($should_always_save && array_key_exists($field_name, $form_data))) {