PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.1.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.1.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 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / includes / Forms / Quote / QuoteFieldRegistration.php

QuoteFieldRegistration.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.1.2, at includes/Forms/Quote/QuoteFieldRegistration.php

670 lines 25.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Quote Field Registration Class
4 *
5 * @package EasyInvoice
6 * @author Your Name
7 * @copyright Copyright (c) 2023, Your Company
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.0.0
10 */
11
12 namespace EasyInvoice\Forms\Quote;
13
14 use EasyInvoice\Forms\BaseFieldRegistration;
15
16 /**
17 * Quote Field Registration
18 *
19 * Handles registration of quote-specific form fields, tabs, and templates.
20 *
21 * @since 1.0.0
22 */
23 class QuoteFieldRegistration extends BaseFieldRegistration {
24
25 /**
26 * Register default tabs
27 *
28 * @since 1.0.0
29 * @return void
30 */
31 public function registerDefaultTabs(): void {
32 // Define all tabs in a single array for easy modification by pro plugins
33 $all_tabs = $this->getDefaultTabsArray();
34
35 // Apply filter to allow pro plugins to modify tabs
36 $all_tabs = apply_filters('easy_invoice_quote_form_tabs', $all_tabs);
37
38 // Register all tabs from the array
39 foreach ($all_tabs as $tab_id => $tab_config) {
40 $this->registerTab($tab_id, $tab_config);
41 }
42 }
43
44 /**
45 * Get default tabs array
46 *
47 * @since 1.0.0
48 * @return array
49 */
50 private function getDefaultTabsArray(): array {
51 return [
52 'quote-tab' => [
53 'label' => __('Quote Details', 'easy-invoice'),
54 'icon' => 'fas fa-file-invoice',
55 'active' => true,
56 'order' => 1
57 ],
58 'items-tab' => [
59 'label' => __('Items', 'easy-invoice'),
60 'icon' => 'fas fa-list',
61 'active' => false,
62 'order' => 2
63 ],
64 'client-tab' => [
65 'label' => __('Client', 'easy-invoice'),
66 'icon' => 'fas fa-user',
67 'active' => false,
68 'order' => 3
69 ],
70 'discounts-taxes-tab' => [
71 'label' => __('Discounts & Taxes', 'easy-invoice'),
72 'icon' => 'fas fa-percentage',
73 'active' => false,
74 'order' => 4
75 ],
76 'payments-tab' => [
77 'label' => __('Payment', 'easy-invoice'),
78 'icon' => 'fas fa-credit-card',
79 'active' => false,
80 'order' => 5
81 ]
82 ];
83 }
84
85 /**
86 * Register default fields
87 *
88 * @since 1.0.0
89 * @return void
90 */
91 public function registerDefaultFields(): void {
92 // Define all fields in a single array for easy modification by pro plugins
93 $all_fields = $this->getDefaultFieldsArray();
94
95 // Apply filter to allow pro plugins to modify fields
96 $all_fields = apply_filters('easy_invoice_quote_form_fields', $all_fields);
97
98 // Register all fields from the array
99 foreach ($all_fields as $field_config) {
100 $tab_id = $field_config['tab_id'] ?? '';
101 unset($field_config['tab_id']); // Remove tab_id from config
102
103 if (!empty($tab_id)) {
104 $this->registerField($tab_id, $field_config);
105 }
106 }
107 }
108
109 /**
110 * Get default fields array
111 *
112 * @since 1.0.0
113 * @return array
114 */
115 private function getDefaultFieldsArray(): array {
116 return [
117 // Quote Tab Fields
118 [
119 'tab_id' => 'quote-tab',
120 'id' => 'quote_template',
121 'type' => 'hidden',
122 'label' => __('Quote Template', 'easy-invoice'),
123 'name' => 'quote_template',
124 'required' => false,
125 'grid_cols' => 'sm:col-span-6',
126 'order' => 0,
127 'default_value' => 'standard',
128 'sanitize_callback' => 'sanitize_text_field',
129 'validate_callback' => function($value) {
130 return !empty(trim($value)) ? true : __('Quote template is required.', 'easy-invoice');
131 }
132 ],
133 [
134 'tab_id' => 'quote-tab',
135 'id' => 'quote_title',
136 'type' => 'text',
137 'label' => __('Quote Title', 'easy-invoice'),
138 'name' => 'title',
139 'placeholder' => __('Enter quote title', 'easy-invoice'),
140 'required' => true,
141 'grid_cols' => 'sm:col-span-6',
142 'order' => 1,
143 'default_value' => __('New Quote', 'easy-invoice'),
144 'sanitize_callback' => 'sanitize_text_field',
145 'validate_callback' => function($value) {
146 return !empty(trim($value)) ? true : __('Quote title is required.', 'easy-invoice');
147 }
148 ],
149 [
150 'tab_id' => 'quote-tab',
151 'id' => 'quote_description',
152 'type' => 'textarea',
153 'label' => __('Quote Description', 'easy-invoice'),
154 'name' => 'description',
155 'placeholder' => __('Enter quote description or additional details', 'easy-invoice'),
156 'required' => false,
157 'grid_cols' => 'sm:col-span-6',
158 'rows' => 3,
159 'order' => 2,
160 'description' => __('Optional description or additional details about this quote.', 'easy-invoice'),
161 'sanitize_callback' => 'sanitize_textarea_field',
162 ],
163 [
164 'tab_id' => 'quote-tab',
165 'id' => 'quote-number',
166 'type' => 'text',
167 'label' => __('Quote Number', 'easy-invoice'),
168 'name' => 'number',
169 'placeholder' => __('QT-001', 'easy-invoice'),
170 'required' => true,
171 'readonly' => true,
172 'description' => __('This is the unique number for your quote. It cannot be changed.', 'easy-invoice'),
173 'grid_cols' => 'sm:col-span-3',
174 'order' => 3,
175 'default_value' => function() {
176 // For new quotes, show the next number without incrementing
177 // For existing quotes, this will be overridden by the quote object's getNumber() method
178 if (class_exists('\\EasyInvoice\\Services\\QuoteNumberService')) {
179 $service = new \EasyInvoice\Services\QuoteNumberService();
180 return $service->getNextNumber();
181 }
182 return 'QT-' . str_pad(time(), 6, '0', STR_PAD_LEFT);
183 },
184 'sanitize_callback' => 'sanitize_text_field',
185 'validate_callback' => function($value) {
186 return !empty(trim($value)) ? true : __('Quote number is required.', 'easy-invoice');
187 }
188 ],
189 [
190 'tab_id' => 'quote-tab',
191 'id' => 'quote-date',
192 'type' => 'date',
193 'label' => __('Quote Date', 'easy-invoice'),
194 'name' => 'issue_date',
195 'required' => true,
196 'grid_cols' => 'sm:col-span-3',
197 'order' => 4,
198 'default_value' => date('Y-m-d'),
199 'sanitize_callback' => 'sanitize_text_field',
200 'validate_callback' => function($value) {
201 return !empty($value) && strtotime($value) ? true : __('Please enter a valid quote date.', 'easy-invoice');
202 }
203 ],
204 [
205 'tab_id' => 'quote-tab',
206 'id' => 'expiry-date',
207 'type' => 'date',
208 'label' => __('Expiry Date', 'easy-invoice'),
209 'name' => 'expiry_date',
210 'required' => true,
211 'grid_cols' => 'sm:col-span-3',
212 'order' => 5,
213 'sanitize_callback' => 'sanitize_text_field',
214 'validate_callback' => function($value) {
215 return !empty($value) && strtotime($value) ? true : __('Please enter a valid expiry date.', 'easy-invoice');
216 }
217 ],
218 [
219 'tab_id' => 'quote-tab',
220 'id' => 'status',
221 'type' => 'select',
222 'label' => __('Status', 'easy-invoice'),
223 'name' => 'status',
224 'required' => true,
225 'grid_cols' => 'sm:col-span-3',
226 'options' => [
227 'available' => __('Available', 'easy-invoice'),
228 'draft' => __('Draft', 'easy-invoice'),
229 'accepted' => __('Accepted', 'easy-invoice'),
230 'declined' => __('Declined', 'easy-invoice'),
231 'cancelled' => __('Cancelled', 'easy-invoice'),
232 'expired' => __('Expired', 'easy-invoice'),
233 'sent' => __('Sent', 'easy-invoice')
234 ],
235 'order' => 5
236 ],
237 [
238 'tab_id' => 'quote-tab',
239 'id' => 'notes',
240 'type' => 'textarea',
241 'label' => __('Notes', 'easy-invoice'),
242 'name' => 'notes',
243 'placeholder' => __('Additional notes for the client', 'easy-invoice'),
244 'grid_cols' => 'sm:col-span-6',
245 'rows' => 3,
246 'section' => 'notes',
247 'order' => 6,
248 'description' => __('These notes will be visible to your client on the quote.', 'easy-invoice'),
249 ],
250 [
251 'tab_id' => 'quote-tab',
252 'id' => 'internal_notes',
253 'type' => 'textarea',
254 'label' => __('Internal Notes', 'easy-invoice'),
255 'name' => 'internal_notes',
256 'placeholder' => __('Internal notes (not visible to client)', 'easy-invoice'),
257 'grid_cols' => 'sm:col-span-6',
258 'rows' => 3,
259 'section' => 'notes',
260 'order' => 7,
261 'description' => __('Only you and your team can see these notes. Clients will not see them.', 'easy-invoice'),
262 ],
263 [
264 'tab_id' => 'quote-tab',
265 'id' => 'terms',
266 'type' => 'textarea',
267 'label' => __('Quote Terms', 'easy-invoice'),
268 'name' => 'terms',
269 'placeholder' => __('Quote terms and conditions', 'easy-invoice'),
270 'grid_cols' => 'sm:col-span-6',
271 'rows' => 3,
272 'section' => 'notes',
273 'order' => 8,
274 'default_value' => function() {
275 return \EasyInvoice\Controllers\SettingsController::getQuoteTermsConditions();
276 },
277 'description' => __('Specify the terms and conditions for this quote.', 'easy-invoice'),
278 ],
279
280 // Client Tab Fields
281 [
282 'tab_id' => 'client-tab',
283 'id' => 'client_id',
284 'type' => 'select',
285 'label' => __('Select Client', 'easy-invoice'),
286 'name' => 'client_id',
287 'required' => false,
288 'grid_cols' => 'sm:col-span-6',
289 'options' => $this->getClientOptions(),
290 'order' => 1,
291 'description' => __('Choose an existing client or add a new one.', 'easy-invoice'),
292 ],
293
294 // Discounts & Taxes Tab Fields
295 [
296 'tab_id' => 'discounts-taxes-tab',
297 'name' => 'discount_type',
298 'type' => 'select',
299 'label' => __('Discount Type', 'easy-invoice'),
300 'grid_cols' => 'sm:col-span-2',
301 'section' => 'discount',
302 'options' => [
303 'none' => __('No Discount', 'easy-invoice'),
304 'percentage' => __('Percentage', 'easy-invoice'),
305 'fixed' => __('Fixed Amount', 'easy-invoice')
306 ],
307 'order' => 1,
308 'description' => __('Choose how the discount should be applied to this quote.', 'easy-invoice'),
309 ],
310 [
311 'tab_id' => 'discounts-taxes-tab',
312 'name' => 'discount_value',
313 'type' => 'number',
314 'label' => __('Discount Value', 'easy-invoice'),
315 'placeholder' => '0.00',
316 'grid_cols' => 'sm:col-span-2',
317 'section' => 'discount',
318 'min' => '0',
319 'step' => '0.01',
320 'order' => 2,
321 'sanitize_callback' => 'floatval',
322 'validate_callback' => function($value) {
323 return is_numeric($value) && $value >= 0 ? true : __('Discount value must be a non-negative number.', 'easy-invoice');
324 },
325 'description' => __('Enter the discount amount or percentage for this quote.', 'easy-invoice'),
326 ],
327 [
328 'tab_id' => 'discounts-taxes-tab',
329 'name' => 'discount_calculation_method',
330 'type' => 'select',
331 'label' => __('Calculation Method', 'easy-invoice'),
332 'grid_cols' => 'sm:col-span-2',
333 'section' => 'tax',
334 'options' => [
335 'before_tax' => __('Before Tax', 'easy-invoice'),
336 'after_tax' => __('After Tax', 'easy-invoice')
337 ],
338 'default_value' => 'before_tax',
339 'order' => 3,
340 'description' => __('Apply discount before tax (reduces taxable amount) or after tax (tax on full amount)', 'easy-invoice'),
341 ],
342 [
343 'tab_id' => 'discounts-taxes-tab',
344 'name' => 'tax_rate',
345 'type' => 'number',
346 'label' => __('Tax Rate (%)', 'easy-invoice'),
347 'placeholder' => '0.00',
348 'grid_cols' => 'sm:col-span-3',
349 'section' => 'tax',
350 'min' => '0',
351 'max' => '100',
352 'step' => '0.01',
353 'order' => 4,
354 'default_value' => function() {
355 return easy_invoice_get_tax_rate();
356 },
357 'sanitize_callback' => 'floatval',
358 'validate_callback' => function($value) {
359 return is_numeric($value) && $value >= 0 && $value <= 100 ? true : __('Tax rate must be between 0 and 100.', 'easy-invoice');
360 },
361 'description' => __('The tax rate that will be applied to taxable items in this quote.', 'easy-invoice'),
362 ],
363 [
364 'tab_id' => 'discounts-taxes-tab',
365 'name' => 'prices_include_tax',
366 'type' => 'select',
367 'label' => __('Price Includes Tax', 'easy-invoice'),
368 'grid_cols' => 'sm:col-span-3',
369 'section' => 'tax',
370 'options' => [
371 'no' => __('No', 'easy-invoice'),
372 'yes' => __('Yes', 'easy-invoice')
373 ],
374 'order' => 5,
375 'description' => __('Choose whether the prices in this quote include tax or not.', 'easy-invoice'),
376 ],
377
378
379 // Payments Tab Fields
380
381 // Currency Section in Payment Tab
382 [
383 'tab_id' => 'payments-tab',
384 'id' => 'currency_code',
385 'type' => 'select',
386 'label' => __('Currency', 'easy-invoice'),
387 'name' => 'currency_code',
388 'grid_cols' => 'sm:col-span-3',
389 'section' => 'currency',
390 'options' => array_merge(
391 ['global' => __('Use from Global Settings', 'easy-invoice')],
392 \EasyInvoice\Helpers\CurrencyHelper::getCurrencyOptions()
393 ),
394 'default_value' => 'global',
395 'order' => 1,
396 'description' => __('Select the currency for this quote or use global settings.', 'easy-invoice'),
397 ],
398 [
399 'tab_id' => 'payments-tab',
400 'id' => 'currency_position',
401 'type' => 'select',
402 'label' => __('Currency Symbol Position', 'easy-invoice'),
403 'name' => 'currency_position',
404 'grid_cols' => 'sm:col-span-3',
405 'section' => 'currency',
406 'options' => [
407 'global' => __('Use from Global Settings', 'easy-invoice'),
408 'before' => __('Before Amount', 'easy-invoice'),
409 'after' => __('After Amount', 'easy-invoice'),
410 ],
411 'default_value' => 'global',
412 'order' => 2,
413 'description' => __('Choose where to display the currency symbol or use global setting.', 'easy-invoice'),
414 ],
415 ];
416 }
417
418 /**
419 * Register default templates
420 *
421 * @since 1.0.0
422 * @return void
423 */
424 public function registerDefaultTemplates(): void {
425 // Define all templates in a single array for easy modification by pro plugins
426 $all_templates = $this->getDefaultTemplatesArray();
427
428 // Apply filter to allow pro plugins to modify templates
429 $all_templates = apply_filters('easy_invoice_quote_templates', $all_templates);
430
431 // Register all templates from the array
432 foreach ($all_templates as $template_id => $template_config) {
433 $this->registerTemplate($template_id, $template_config);
434 }
435 }
436
437 /**
438 * Get default templates array
439 *
440 * @since 1.0.0
441 * @return array
442 */
443 private function getDefaultTemplatesArray(): array {
444 // Check if this is the free version (no pro plugin active)
445 $is_free_version = !easy_invoice_has_pro();
446
447 return [
448 'standard' => [
449 'name' => __('Standard', 'easy-invoice'),
450 'description' => __('A clean, professional quote template', 'easy-invoice'),
451 'icon' => 'fas fa-file-invoice',
452 'order' => 1,
453 'is_free' => true
454 ],
455 'legacy' => [
456 'name' => __('Legacy', 'easy-invoice'),
457 'description' => __('Clean, minimalist design with traditional business layout', 'easy-invoice'),
458 'icon' => 'fas fa-file-alt',
459 'order' => 2,
460 'is_free' => true
461 ],
462 'modern' => [
463 'name' => __('Modern', 'easy-invoice'),
464 'description' => __('A contemporary quote template', 'easy-invoice'),
465 'icon' => 'fas fa-star',
466 'order' => 3,
467 'is_free' => !$is_free_version
468 ],
469 'minimal' => [
470 'name' => __('Minimal', 'easy-invoice'),
471 'description' => __('A simple, clean quote template', 'easy-invoice'),
472 'icon' => 'fas fa-minus',
473 'order' => 4,
474 'is_free' => !$is_free_version
475 ]
476 ];
477 }
478
479 /**
480 * Register default item fields
481 *
482 * @since 1.0.0
483 * @return void
484 */
485 public function registerDefaultItemFields(): void {
486 // Define all item fields in a single array for easy modification by pro plugins
487 $all_item_fields = $this->getDefaultItemFieldsArray();
488
489 // Apply filter to allow pro plugins to modify item fields
490 $all_item_fields = apply_filters('easy_invoice_quote_item_fields', $all_item_fields);
491
492 // Register all item fields from the array
493 foreach ($all_item_fields as $field_config) {
494 $this->registerItemField($field_config);
495 }
496 }
497
498 /**
499 * Get default item fields array
500 *
501 * @since 1.0.0
502 * @return array
503 */
504 private function getDefaultItemFieldsArray(): array {
505 $fields = [
506 [
507 'id' => 'title',
508 'type' => 'text',
509 'label' => __('Item Title', 'easy-invoice'),
510 'name' => 'title',
511 'placeholder' => __('Enter item title', 'easy-invoice'),
512 'required' => true,
513 'container_class' => 'sm:col-span-6',
514 'order' => 1,
515 'description' => __('A short name for this item (e.g. Service Fee, Product Name).', 'easy-invoice'),
516 ],
517 [
518 'id' => 'description',
519 'type' => 'textarea',
520 'label' => __('Description', 'easy-invoice'),
521 'name' => 'description',
522 'placeholder' => __('Enter item description', 'easy-invoice'),
523 'container_class' => 'sm:col-span-6',
524 'rows' => 3,
525 'order' => 2,
526 'description' => __('Details about this item for your client.', 'easy-invoice'),
527 ],
528 [
529 'id' => 'quantity',
530 'type' => 'number',
531 'label' => __('Quantity', 'easy-invoice'),
532 'name' => 'quantity',
533 'placeholder' => '1',
534 'required' => true,
535 'container_class' => 'sm:col-span-1',
536 'min' => '0',
537 'step' => '0.01',
538 'default_value' => '1',
539 'order' => 3,
540 'description' => __('How many units of this item are being billed?', 'easy-invoice'),
541 ],
542 [
543 'id' => 'price',
544 'type' => 'number',
545 'label' => __('Price', 'easy-invoice'),
546 'name' => 'price',
547 'placeholder' => '0.00',
548 'required' => true,
549 'container_class' => 'sm:col-span-1',
550 'min' => '0',
551 'step' => '0.01',
552 'default_value' => '0.00',
553 'order' => 4,
554 'description' => __('Unit price for this item.', 'easy-invoice'),
555 ]
556 ];
557
558 // Add adjust field only if setting allows it
559 if (\EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField()) {
560 $fields[] = [
561 'id' => 'adjust_percentage',
562 'type' => 'number',
563 'label' => __('Adjust (%)', 'easy-invoice'),
564 'name' => 'adjust_percentage',
565 'placeholder' => '0.00',
566 'container_class' => 'sm:col-span-1',
567 'min' => '-100',
568 'max' => '1000',
569 'step' => '0.01',
570 'default_value' => '0.00',
571 'order' => 5,
572 'description' => __('Percentage adjustment to apply to this item (positive for markup, negative for discount).', 'easy-invoice'),
573 ];
574 $current_order = 5; // Adjust field is present
575 } else {
576 $current_order = 4; // Adjust field is not present, start after price field
577 }
578
579 // Calculate order for remaining fields
580
581 $fields[] = [
582 'id' => 'total',
583 'type' => 'number',
584 'label' => __('Total', 'easy-invoice'),
585 'name' => 'total',
586 'placeholder' => '0.00',
587 'container_class' => 'sm:col-span-1',
588 'min' => '0',
589 'step' => '0.01',
590 'default_value' => '0.00',
591 'readonly' => true,
592 'order' => $current_order + 1,
593 'description' => __('Total amount for this item (Quantity x Price) with adjustment applied.', 'easy-invoice'),
594 ];
595
596 $fields[] = [
597 'id' => 'taxable',
598 'type' => 'checkbox',
599 'label' => __('Taxable', 'easy-invoice'),
600 'name' => 'taxable',
601 'container_class' => 'sm:col-span-6',
602 'default_value' => '1',
603 'order' => $current_order + 2,
604 'description' => __('Check if this item is subject to tax.', 'easy-invoice'),
605 ];
606
607 return $fields;
608 }
609
610 /**
611 * Get client options for select field
612 *
613 * @since 1.0.0
614 * @return array
615 */
616 private function getClientOptions(): array {
617 $options = ['' => __('Select a client', 'easy-invoice')];
618
619 // Get clients from repository
620 $client_repository = new \EasyInvoice\Repositories\ClientRepository();
621 $clients = $client_repository->all();
622
623 foreach ($clients as $client) {
624 $client_name = $client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName());
625 $options[$client->getId()] = $client_name;
626 }
627
628 return $options;
629 }
630
631 /**
632 * Get the filter name for tabs
633 *
634 * @since 1.0.0
635 * @return string
636 */
637 protected function getTabFilterName(): string {
638 return 'easy_invoice_quote_register_tab';
639 }
640
641 /**
642 * Get the filter name for fields
643 *
644 * @since 1.0.0
645 * @return string
646 */
647 protected function getFieldFilterName(): string {
648 return 'easy_invoice_quote_register_field';
649 }
650
651 /**
652 * Get the filter name for templates
653 *
654 * @since 1.0.0
655 * @return string
656 */
657 protected function getTemplateFilterName(): string {
658 return 'easy_invoice_quote_register_template';
659 }
660
661 /**
662 * Get the filter name for item fields
663 *
664 * @since 1.0.0
665 * @return string
666 */
667 protected function getItemFieldFilterName(): string {
668 return 'easy_invoice_quote_register_item_field';
669 }
670 }