| @@ -90,9 +90,21 @@ | ||
| 90 | 90 | */ |
| 91 | 91 | public function registerDefaultFields(): void { |
| 92 | 92 | // Define all fields in a single array for easy modification by pro plugins |
| 93 | 93 | $all_fields = $this->getDefaultFieldsArray(); |
| 94 | - | |
| 94 | + | |
| 95 | + // Gate basic tax fields on the global Settings → Tax → Enable | |
| 96 | + // Tax setting — matches the invoice builder. When tax is | |
| 97 | + // globally off, the override checkbox / rate / prices-include-tax | |
| 98 | + // are all hidden from the quote builder. | |
| 99 | + if (get_option('easy_invoice_tax_enabled', 'no') !== 'yes') { | |
| 100 | + $tax_field_names = ['tax_enabled', 'tax_rate', 'prices_include_tax']; | |
| 101 | + $all_fields = array_values(array_filter( | |
| 102 | + $all_fields, | |
| 103 | + static fn($f) => !in_array($f['name'] ?? '', $tax_field_names, true) | |
| 104 | + )); | |
| 105 | + } | |
| 106 | + | |
| 95 | 107 | // Apply filter to allow pro plugins to modify fields |
| 96 | 108 | $all_fields = apply_filters('easy_invoice_quote_form_fields', $all_fields); |
| 97 | 109 | |
| 98 | 110 | // Register all fields from the array |
| @@ -194,9 +206,9 @@ | ||
| 194 | 206 | 'name' => 'issue_date', |
| 195 | 207 | 'required' => true, |
| 196 | 208 | 'grid_cols' => 'sm:col-span-3', |
| 197 | 209 | 'order' => 4, |
| 198 | - 'default_value' => date('Y-m-d'), | |
| 210 | + 'default_value' => current_time('Y-m-d'), | |
| 199 | 211 | 'sanitize_callback' => 'sanitize_text_field', |
| 200 | 212 | 'validate_callback' => function($value) { |
| 201 | 213 | return !empty($value) && strtotime($value) ? true : __('Please enter a valid quote date.', 'easy-invoice'); |
| 202 | 214 | } |
| @@ -209,8 +221,17 @@ | ||
| 209 | 221 | 'name' => 'expiry_date', |
| 210 | 222 | 'required' => true, |
| 211 | 223 | 'grid_cols' => 'sm:col-span-3', |
| 212 | 224 | 'order' => 5, |
| 225 | + // Required but shipped with no default, so saving a brand-new quote | |
| 226 | + // always failed first time with "Expiry Date is required" — the same | |
| 227 | + // defect the invoice form had with its issue/due dates. The quote date | |
| 228 | + // field just above already defaults; this one was missed. | |
| 229 | + // | |
| 230 | + // 30 days matches the validity period used elsewhere in the plugin | |
| 231 | + // (see QuoteController's quote defaults). There is no site setting for | |
| 232 | + // quote validity yet; when one is added, read it here. | |
| 233 | + 'default_value' => gmdate('Y-m-d', strtotime('+30 days', strtotime(current_time('Y-m-d')))), | |
| 213 | 234 | 'sanitize_callback' => 'sanitize_text_field', |
| 214 | 235 | 'validate_callback' => function($value) { |
| 215 | 236 | return !empty($value) && strtotime($value) ? true : __('Please enter a valid expiry date.', 'easy-invoice'); |
| 216 | 237 | } |
| @@ -324,14 +345,37 @@ | ||
| 324 | 345 | }, |
| 325 | 346 | 'description' => __('Enter the discount amount or percentage for this quote.', 'easy-invoice'), |
| 326 | 347 | ], |
| 327 | 348 | [ |
| 349 | + // Per-quote "Enable Tax" override — mirrors the | |
| 350 | + // identical field on the invoice builder. See | |
| 351 | + // InvoiceFieldRegistration for the design rationale. | |
| 328 | 352 | 'tab_id' => 'discounts-taxes-tab', |
| 353 | + 'name' => 'tax_enabled', | |
| 354 | + 'type' => 'checkbox', | |
| 355 | + 'label' => __('Apply Tax to This Quote', 'easy-invoice'), | |
| 356 | + 'grid_cols' => 'sm:col-span-6', | |
| 357 | + 'section' => 'tax', | |
| 358 | + 'order' => 2.5, | |
| 359 | + 'default_value' => function() { | |
| 360 | + return get_option('easy_invoice_tax_enabled', 'no') === 'yes' ? 'yes' : 'no'; | |
| 361 | + }, | |
| 362 | + 'sanitize_callback' => function($value) { | |
| 363 | + if ($value === 'yes' || $value === '1' || $value === 1 || $value === true || $value === 'on') return 'yes'; | |
| 364 | + return 'no'; | |
| 365 | + }, | |
| 366 | + 'description' => __('Override the global tax setting for this quote.', 'easy-invoice'), | |
| 367 | + ], | |
| 368 | + [ | |
| 369 | + // Discount calculation timing — belongs in the discount | |
| 370 | + // section (controls when discount applies relative to | |
| 371 | + // tax, not how tax is calculated). | |
| 372 | + 'tab_id' => 'discounts-taxes-tab', | |
| 329 | 373 | 'name' => 'discount_calculation_method', |
| 330 | 374 | 'type' => 'select', |
| 331 | - 'label' => __('Calculation Method', 'easy-invoice'), | |
| 375 | + 'label' => __('Discount Timing', 'easy-invoice'), | |
| 332 | 376 | 'grid_cols' => 'sm:col-span-2', |
| 333 | - 'section' => 'tax', | |
| 377 | + 'section' => 'discount', | |
| 334 | 378 | 'options' => [ |
| 335 | 379 | 'before_tax' => __('Before Tax', 'easy-invoice'), |
| 336 | 380 | 'after_tax' => __('After Tax', 'easy-invoice') |
| 337 | 381 | ], |
| @@ -339,8 +383,11 @@ | ||
| 339 | 383 | 'order' => 3, |
| 340 | 384 | 'description' => __('Apply discount before tax (reduces taxable amount) or after tax (tax on full amount)', 'easy-invoice'), |
| 341 | 385 | ], |
| 342 | 386 | [ |
| 387 | + // Tax sub-fields sit on one row (3 + 3 = 6 cols) so | |
| 388 | + // Tax Rate and Price Includes Tax appear side-by-side | |
| 389 | + // under the Apply Tax checkbox. | |
| 343 | 390 | 'tab_id' => 'discounts-taxes-tab', |
| 344 | 391 | 'name' => 'tax_rate', |
| 345 | 392 | 'type' => 'number', |
| 346 | 393 | 'label' => __('Tax Rate (%)', 'easy-invoice'), |