PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
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 / Invoice / InvoiceFieldRegistration.php

InvoiceFieldRegistration.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.0, at includes/Forms/Invoice/InvoiceFieldRegistration.php

760 lines 30.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Invoice 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\Invoice;
13
14 use EasyInvoice\Forms\BaseFieldRegistration;
15
16 /**
17 * Invoice Field Registration
18 *
19 * Handles registration of invoice-specific form fields, tabs, and templates.
20 *
21 * @since 1.0.0
22 */
23 class InvoiceFieldRegistration 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_invoice_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 'invoice-tab' => [
53 'label' => __('Invoice 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 // Gate the basic tax fields on the global Settings → Tax →
96 // Enable Tax setting. When tax is globally off, the entire
97 // tax block (override checkbox, tax rate, prices include tax)
98 // is hidden from the invoice builder — the merchant has to
99 // turn tax on globally before any tax controls surface here.
100 // discount_calculation_method stays (it lives in the discount
101 // section, controls discount timing, not tax).
102 if (get_option('easy_invoice_tax_enabled', 'no') !== 'yes') {
103 $tax_field_names = ['tax_enabled', 'tax_rate', 'prices_include_tax'];
104 $all_fields = array_values(array_filter(
105 $all_fields,
106 static fn($f) => !in_array($f['name'] ?? '', $tax_field_names, true)
107 ));
108 }
109
110 // Apply filter to allow pro plugins to modify fields
111 $all_fields = apply_filters('easy_invoice_invoice_fields', $all_fields);
112
113 // Register all fields from the array
114 foreach ($all_fields as $field_config) {
115 $this->registerField($field_config['tab_id'], $field_config);
116 }
117 }
118
119 /**
120 * Get default fields array
121 *
122 * @since 1.0.0
123 * @return array
124 */
125 private function getDefaultFieldsArray(): array {
126 return [
127 // Invoice Tab Fields
128 [
129 'tab_id' => 'invoice-tab',
130 'name' => 'invoice_template',
131 'type' => 'hidden',
132 'label' => __('Invoice Template', 'easy-invoice'),
133 'required' => false,
134 'grid_cols' => 'sm:col-span-6',
135 'order' => 0,
136 'default_value' => 'standard',
137 'sanitize_callback' => 'sanitize_text_field',
138 'validate_callback' => function($value) {
139 return !empty(trim($value)) ? true : __('Invoice template is required.', 'easy-invoice');
140 }
141 ],
142 [
143 'tab_id' => 'invoice-tab',
144 'name' => 'title',
145 'type' => 'text',
146 'label' => __('Invoice Title', 'easy-invoice'),
147 'placeholder' => __('Enter invoice title', 'easy-invoice'),
148 'required' => true,
149 'grid_cols' => 'sm:col-span-6',
150 'order' => 1,
151 'sanitize_callback' => 'sanitize_text_field',
152 'validate_callback' => function($value) {
153 return !empty(trim($value)) ? true : __('Invoice title is required.', 'easy-invoice');
154 }
155 ],
156 [
157 'tab_id' => 'invoice-tab',
158 'name' => 'description',
159 'type' => 'textarea',
160 'label' => __('Invoice Description', 'easy-invoice'),
161 'placeholder' => __('Enter invoice description or additional details', 'easy-invoice'),
162 'required' => false,
163 'grid_cols' => 'sm:col-span-6',
164 'rows' => 3,
165 'order' => 2,
166 'description' => __('Optional description or additional details about this invoice.', 'easy-invoice'),
167 'sanitize_callback' => 'sanitize_textarea_field',
168 ],
169 [
170 'tab_id' => 'invoice-tab',
171 'name' => 'number',
172 'type' => 'text',
173 'label' => __('Invoice Number', 'easy-invoice'),
174 'placeholder' => __('INV-001', 'easy-invoice'),
175 'required' => true,
176 'readonly' => true,
177 'description' => __('This is the unique number for your invoice. It cannot be changed.', 'easy-invoice'),
178 'grid_cols' => 'sm:col-span-3',
179 'order' => 3,
180 'default_value' => function() {
181 // For new invoices, show the next number without incrementing
182 // For existing invoices, this will be overridden by the invoice object's getNumber() method
183 if (class_exists('\\EasyInvoice\\Services\\InvoiceNumberService')) {
184 $service = new \EasyInvoice\Services\InvoiceNumberService();
185 return $service->getNextNumber();
186 }
187 return 'INV-' . str_pad(time(), 6, '0', STR_PAD_LEFT);
188 },
189 'sanitize_callback' => 'sanitize_text_field',
190 'validate_callback' => function($value) {
191 return !empty(trim($value)) ? true : __('Invoice number is required.', 'easy-invoice');
192 }
193 ],
194 [
195 'tab_id' => 'invoice-tab',
196 'name' => 'issue_date',
197 'type' => 'date',
198 'label' => __('Issue Date', 'easy-invoice'),
199 'required' => true,
200 'grid_cols' => 'sm:col-span-3',
201 'order' => 4,
202 // Both date fields are `required` but shipped with no default, so a
203 // brand-new invoice always failed its first save with "Issue Date is
204 // required / Due Date is required" — while the live preview beside the
205 // form was already showing dates, which made the error look wrong.
206 // The quote form has always defaulted its date this way.
207 //
208 // current_time() rather than gmdate(): gmdate() uses the server timezone,
209 // so a site ahead of or behind UTC could get yesterday's or tomorrow's
210 // date on a new invoice.
211 'default_value' => current_time('Y-m-d'),
212 'sanitize_callback' => 'sanitize_text_field',
213 'validate_callback' => function($value) {
214 return !empty($value) && strtotime($value) ? true : __('Please enter a valid issue date.', 'easy-invoice');
215 }
216 ],
217 [
218 'tab_id' => 'invoice-tab',
219 'name' => 'due_date',
220 'type' => 'date',
221 'label' => __('Due Date', 'easy-invoice'),
222 'required' => true,
223 'grid_cols' => 'sm:col-span-3',
224 'order' => 5,
225 // Defaults to issue date + the configured payment term
226 // (Settings → Invoice → "Due days", default 30) rather than a
227 // hardcoded value, so the prefilled date matches the terms the site
228 // actually bills on. See the note on issue_date above.
229 'default_value' => gmdate('Y-m-d', strtotime(
230 '+' . max(0, (int) get_option('easy_invoice_invoice_due_days', 30)) . ' days',
231 strtotime(current_time('Y-m-d'))
232 )),
233 'sanitize_callback' => 'sanitize_text_field',
234 'validate_callback' => function($value) {
235 return !empty($value) && strtotime($value) ? true : __('Please enter a valid due date.', 'easy-invoice');
236 }
237 ],
238 [
239 'tab_id' => 'invoice-tab',
240 'name' => 'status',
241 'type' => 'select',
242 'label' => __('Status', 'easy-invoice'),
243 'required' => true,
244 'grid_cols' => 'sm:col-span-3',
245 'options' => [
246 'available' => __('Available', 'easy-invoice'),
247 'draft' => __('Draft', 'easy-invoice'),
248 'overdue' => __('Overdue', 'easy-invoice'),
249 'paid' => __('Paid', 'easy-invoice'),
250 'unpaid' => __('Unpaid', 'easy-invoice'),
251 'cancelled' => __('Cancelled', 'easy-invoice')
252 ],
253 'order' => 6
254 ],
255 // Customer tax identity. FormProcessor stores a field as
256 // '_easy_invoice_' . $name, so these land on the meta keys
257 // Services\TaxTreatment reads when deciding whether an invoice is a
258 // cross-border reverse charge or an export.
259 [
260 'tab_id' => 'invoice-tab',
261 'name' => 'customer_vat_number',
262 'type' => 'text',
263 'label' => __('Customer VAT number', 'easy-invoice'),
264 'placeholder' => __('e.g. FR12345678901', 'easy-invoice'),
265 'grid_cols' => 'sm:col-span-3',
266 'section' => 'notes',
267 'order' => 4,
268 'description' => __('Needed to invoice a VAT-registered business in another country without charging VAT.', 'easy-invoice'),
269 ],
270 [
271 'tab_id' => 'invoice-tab',
272 'name' => 'customer_country',
273 'type' => 'text',
274 'label' => __('Customer country', 'easy-invoice'),
275 'placeholder' => __('e.g. FR', 'easy-invoice'),
276 'grid_cols' => 'sm:col-span-3',
277 'section' => 'notes',
278 'order' => 5,
279 'description' => __('Two-letter country code.', 'easy-invoice'),
280 ],
281 [
282 'tab_id' => 'invoice-tab',
283 'name' => 'notes',
284 'type' => 'textarea',
285 'label' => __('Notes', 'easy-invoice'),
286 'placeholder' => __('Additional notes for the client', 'easy-invoice'),
287 'grid_cols' => 'sm:col-span-6',
288 'rows' => 3,
289 'section' => 'notes',
290 'order' => 6,
291 'description' => __('These notes will be visible to your client on the invoice.', 'easy-invoice'),
292 ],
293 [
294 'tab_id' => 'invoice-tab',
295 'name' => 'internal_notes',
296 'type' => 'textarea',
297 'label' => __('Internal Notes', 'easy-invoice'),
298 'placeholder' => __('Internal notes (not visible to client)', 'easy-invoice'),
299 'grid_cols' => 'sm:col-span-6',
300 'rows' => 3,
301 'section' => 'notes',
302 'order' => 7,
303 'description' => __('Only you and your team can see these notes. Clients will not see them.', 'easy-invoice'),
304 ],
305 [
306 'tab_id' => 'invoice-tab',
307 'name' => 'terms',
308 'type' => 'textarea',
309 'label' => __('Payment Terms', 'easy-invoice'),
310 'placeholder' => __('Payment terms and conditions', 'easy-invoice'),
311 'grid_cols' => 'sm:col-span-6',
312 'rows' => 3,
313 'section' => 'notes',
314 'order' => 8,
315 'default_value' => function() {
316 return \EasyInvoice\Controllers\SettingsController::getInvoiceTermsConditions();
317 },
318 'description' => __('Specify the payment terms and conditions for this invoice.', 'easy-invoice'),
319 ],
320
321
322 // Client Tab Fields
323 [
324 'tab_id' => 'client-tab',
325 'name' => 'client_id',
326 'type' => 'select',
327 'label' => __('Select Client', 'easy-invoice'),
328 'required' => false,
329 'grid_cols' => 'sm:col-span-6',
330 'options' => $this->getClientOptions(),
331 'order' => 1,
332 'description' => __('Choose an existing client or add a new one.', 'easy-invoice'),
333 ],
334
335 // Discounts & Taxes Tab Fields
336 [
337 'tab_id' => 'discounts-taxes-tab',
338 'name' => 'discount_type',
339 'type' => 'select',
340 'label' => __('Discount Type', 'easy-invoice'),
341 'grid_cols' => 'sm:col-span-2',
342 'section' => 'discount',
343 'options' => [
344 'none' => __('No Discount', 'easy-invoice'),
345 'percentage' => __('Percentage', 'easy-invoice'),
346 'fixed' => __('Fixed Amount', 'easy-invoice')
347 ],
348 'order' => 1,
349 'description' => __('Choose how the discount should be applied to this invoice.', 'easy-invoice'),
350 ],
351 [
352 'tab_id' => 'discounts-taxes-tab',
353 'name' => 'discount_value',
354 'type' => 'number',
355 'label' => __('Discount Value', 'easy-invoice'),
356 'placeholder' => '0.00',
357 'grid_cols' => 'sm:col-span-2',
358 'section' => 'discount',
359 'min' => '0',
360 'step' => '0.01',
361 'order' => 2,
362 'sanitize_callback' => 'floatval',
363 'validate_callback' => function($value) {
364 return is_numeric($value) && $value >= 0 ? true : __('Discount value must be a non-negative number.', 'easy-invoice');
365 },
366 'description' => __('Enter the discount amount or percentage for this invoice.', 'easy-invoice'),
367 ],
368 [
369 // Per-invoice "Enable Tax" override.
370 //
371 // Defaults to the global Settings → Tax → Enable Tax
372 // checkbox value. The user can flip it for a specific
373 // invoice — handy for tax-exempt customers (override
374 // "on" globally → "off" here) or one-off taxable
375 // invoices when tax is globally off.
376 //
377 // calculateTotals() in the Invoice model treats the
378 // tax_rate as 0 when this is unchecked, regardless of
379 // what value sits in the tax_rate field — so the rest
380 // of this section can stay visible without applying
381 // tax until the user opts in.
382 'tab_id' => 'discounts-taxes-tab',
383 'name' => 'tax_enabled',
384 'type' => 'checkbox',
385 'label' => __('Apply Tax to This Invoice', 'easy-invoice'),
386 'grid_cols' => 'sm:col-span-6',
387 'section' => 'tax',
388 'order' => 2.5,
389 'default_value' => function() {
390 // Default to global setting. Saved values override.
391 return get_option('easy_invoice_tax_enabled', 'no') === 'yes' ? 'yes' : 'no';
392 },
393 'sanitize_callback' => function($value) {
394 // Coerce any truthy input ('yes', '1', 1, true, 'on')
395 // to 'yes'; anything else to 'no'. Stored as a
396 // string so the field-save loop persists '' as 'no'
397 // (PHP checkbox unchecked = field absent from POST).
398 if ($value === 'yes' || $value === '1' || $value === 1 || $value === true || $value === 'on') return 'yes';
399 return 'no';
400 },
401 'description' => __('Override the global tax setting for this invoice. Defaults to whatever is configured in Settings → Tax.', 'easy-invoice'),
402 ],
403 // Discount calculation timing — belongs in the discount
404 // section (it controls when the discount applies relative
405 // to tax, not how tax is calculated). Pinned next to the
406 // Discount Type / Value fields above so the grouping is
407 // intuitive.
408 [
409 'tab_id' => 'discounts-taxes-tab',
410 'name' => 'discount_calculation_method',
411 'type' => 'select',
412 'label' => __('Discount Timing', 'easy-invoice'),
413 'grid_cols' => 'sm:col-span-2',
414 'section' => 'discount',
415 'options' => [
416 'before_tax' => __('Before Tax', 'easy-invoice'),
417 'after_tax' => __('After Tax', 'easy-invoice')
418 ],
419 'order' => 3,
420 'description' => __('Apply discount before tax (reduces taxable amount) or after tax (tax on full amount)', 'easy-invoice'),
421 ],
422
423 // Tax sub-fields below sit on one row (2 + 2 + 2 = 6 cols)
424 // so the user sees Tax Rate and Price Includes Tax
425 // side-by-side under the Apply Tax checkbox.
426 [
427 'tab_id' => 'discounts-taxes-tab',
428 'name' => 'tax_rate',
429 'type' => 'number',
430 'label' => __('Tax Rate (%)', 'easy-invoice'),
431 'placeholder' => '0.00',
432 'grid_cols' => 'sm:col-span-3',
433 'section' => 'tax',
434 'min' => '0',
435 'max' => '100',
436 'step' => '0.01',
437 'order' => 4,
438 'default_value' => function() {
439 return easy_invoice_get_tax_rate();
440 },
441 'sanitize_callback' => 'floatval',
442 'validate_callback' => function($value) {
443 return is_numeric($value) && $value >= 0 && $value <= 100 ? true : __('Tax rate must be between 0 and 100.', 'easy-invoice');
444 },
445 'description' => __('The tax rate that will be applied to taxable items in this invoice.', 'easy-invoice'),
446 ],
447
448 [
449 'tab_id' => 'discounts-taxes-tab',
450 'name' => 'prices_include_tax',
451 'type' => 'select',
452 'label' => __('Price Includes Tax', 'easy-invoice'),
453 'grid_cols' => 'sm:col-span-3',
454 'section' => 'tax',
455 'options' => [
456 'no' => __('No', 'easy-invoice'),
457 'yes' => __('Yes', 'easy-invoice')
458 ],
459 'order' => 5,
460 'description' => __('Choose whether the prices in this invoice include tax or not.', 'easy-invoice'),
461 ],
462
463 // Payments Tab Fields
464 [
465 'tab_id' => 'payments-tab',
466 'name' => 'payment_gateways',
467 'type' => 'payment_gateways',
468 'label' => __('Payment Gateways', 'easy-invoice'),
469 'grid_cols' => 'sm:col-span-12',
470 'section' => 'payment',
471 'order' => 1
472 ],
473 [
474 'tab_id' => 'payments-tab',
475 'name' => 'payment_gateways_toggle_state',
476 'type' => 'hidden',
477 'label' => __('Payment Gateways Toggle State', 'easy-invoice'),
478 'default_value' => '0',
479 'section' => 'payment',
480 'order' => 2
481 ],
482
483 // Currency Section in Payment Tab
484 [
485 'tab_id' => 'payments-tab',
486 'name' => 'currency_code',
487 'type' => 'select',
488 'label' => __('Currency', 'easy-invoice'),
489 'grid_cols' => 'sm:col-span-3',
490 'section' => 'currency',
491 'options' => array_merge(
492 ['global' => __('Use from Global Settings', 'easy-invoice')],
493 \EasyInvoice\Helpers\CurrencyHelper::getCurrencyOptions()
494 ),
495 'default_value' => 'global',
496 'order' => 2,
497 'description' => __('Select the currency for this invoice or use global settings.', 'easy-invoice'),
498 ],
499 [
500 'tab_id' => 'payments-tab',
501 'name' => 'currency_position',
502 'type' => 'select',
503 'label' => __('Currency Symbol Position', 'easy-invoice'),
504 'grid_cols' => 'sm:col-span-3',
505 'section' => 'currency',
506 'options' => [
507 'global' => __('Use from Global Settings', 'easy-invoice'),
508 'before' => __('Before Amount', 'easy-invoice'),
509 'after' => __('After Amount', 'easy-invoice'),
510 ],
511 'default_value' => 'global',
512 'order' => 3,
513 'description' => __('Choose where to display the currency symbol or use global setting.', 'easy-invoice'),
514 ],
515 ];
516 }
517
518 /**
519 * Register default templates
520 *
521 * @since 1.0.0
522 * @return void
523 */
524 public function registerDefaultTemplates(): void {
525 // Define all templates in a single array for easy modification by pro plugins
526 $all_templates = $this->getDefaultTemplatesArray();
527
528 // Apply filter to allow pro plugins to modify templates
529 $all_templates = apply_filters('easy_invoice_invoice_templates', $all_templates);
530
531 // Register all templates from the array
532 foreach ($all_templates as $template_id => $template_config) {
533 $this->registerTemplate($template_id, $template_config);
534 }
535 }
536
537 /**
538 * Get default templates array
539 *
540 * @since 1.0.0
541 * @return array
542 */
543 private function getDefaultTemplatesArray(): array {
544 // Check if this is the free version (no pro plugin active)
545 $is_free_version = !easy_invoice_has_pro();
546
547 return [
548 'standard' => [
549 'name' => __('Standard', 'easy-invoice'),
550 'icon' => 'fas fa-file-invoice',
551 'description' => __('Clean and professional standard template', 'easy-invoice'),
552 'order' => 1,
553 'is_free' => true
554 ],
555 'legacy' => [
556 'name' => __('Legacy', 'easy-invoice'),
557 'icon' => 'fas fa-file-alt',
558 'description' => __('Clean, minimalist design with traditional business layout', 'easy-invoice'),
559 'order' => 2,
560 'is_free' => true
561 ],
562 'professional' => [
563 'name' => __('Professional', 'easy-invoice'),
564 'icon' => 'fas fa-briefcase',
565 'description' => __('Professional business template with modern design', 'easy-invoice'),
566 'order' => 3,
567 'is_free' => !$is_free_version
568 ],
569 'modern' => [
570 'name' => __('Modern', 'easy-invoice'),
571 'icon' => 'fas fa-rocket',
572 'description' => __('Modern and sleek design template', 'easy-invoice'),
573 'order' => 4,
574 'is_free' => !$is_free_version
575 ]
576 ];
577 }
578
579 /**
580 * Register default item fields
581 *
582 * @since 1.0.0
583 * @return void
584 */
585 public function registerDefaultItemFields(): void {
586 // Define all item fields in a single array for easy modification by pro plugins
587 $all_item_fields = $this->getDefaultItemFieldsArray();
588
589 // Apply filter to allow pro plugins to modify item fields
590 $all_item_fields = apply_filters('easy_invoice_invoice_item_fields', $all_item_fields);
591
592 // Register all item fields from the array
593 foreach ($all_item_fields as $field_config) {
594 $this->registerItemField($field_config);
595 }
596 }
597
598 /**
599 * Get default item fields array
600 *
601 * @since 1.0.0
602 * @return array
603 */
604 private function getDefaultItemFieldsArray(): array {
605 $fields = [
606 [
607 'name' => 'title',
608 'type' => 'text',
609 'label' => __('Item Title', 'easy-invoice'),
610 'placeholder' => __('Enter item title', 'easy-invoice'),
611 'required' => true,
612 'container_class' => 'sm:col-span-6',
613 'order' => 1,
614 'description' => __('A short name for this item (e.g. Service Fee, Product Name).', 'easy-invoice'),
615 ],
616 [
617 'name' => 'description',
618 'type' => 'textarea',
619 'label' => __('Description', 'easy-invoice'),
620 'placeholder' => __('Enter item description', 'easy-invoice'),
621 'container_class' => 'sm:col-span-6',
622 'rows' => 3,
623 'order' => 2,
624 'description' => __('Details about this item for your client.', 'easy-invoice'),
625 ],
626 [
627 'name' => 'quantity',
628 'type' => 'number',
629 'label' => __('Quantity', 'easy-invoice'),
630 'placeholder' => '0',
631 'required' => true,
632 'container_class' => 'sm:col-span-1',
633 'min' => '0',
634 'step' => '0.01',
635 'default_value' => '0',
636 'order' => 3,
637 'description' => __('How many units of this item are being billed?', 'easy-invoice'),
638 ],
639 [
640 'name' => 'price',
641 'type' => 'number',
642 'label' => __('Price', 'easy-invoice'),
643 'placeholder' => '0.00',
644 'required' => true,
645 'container_class' => 'sm:col-span-1',
646 'min' => '0',
647 'step' => '0.01',
648 'default_value' => '0.00',
649 'order' => 4,
650 'description' => __('Unit price for this item.', 'easy-invoice'),
651 ]
652 ];
653
654 // Add adjust field only if setting allows it
655 if (\EasyInvoice\Controllers\SettingsController::shouldShowInvoiceAdjustField()) {
656 $fields[] = [
657 'name' => 'adjust_percentage',
658 'type' => 'number',
659 'label' => __('Adjust (%)', 'easy-invoice'),
660 'placeholder' => '0.00',
661 'container_class' => 'sm:col-span-1',
662 'min' => '-100',
663 'max' => '1000',
664 'step' => '0.01',
665 'default_value' => '0.00',
666 'order' => 5,
667 'description' => __('Percentage adjustment to apply to this item (positive for markup, negative for discount).', 'easy-invoice'),
668 ];
669 }
670
671 // Add total field
672 $fields[] = [
673 'name' => 'total',
674 'type' => 'number',
675 'label' => __('Total', 'easy-invoice'),
676 'placeholder' => '0.00',
677 'container_class' => 'sm:col-span-1',
678 'min' => '0',
679 'step' => '0.01',
680 'default_value' => '0.00',
681 'readonly' => true,
682 'order' => \EasyInvoice\Controllers\SettingsController::shouldShowInvoiceAdjustField() ? 6 : 5,
683 'description' => __('Total amount for this item (Quantity x Price) with adjustment applied.', 'easy-invoice'),
684 ];
685
686 // Add taxable field
687 $fields[] = [
688 'name' => 'taxable',
689 'type' => 'checkbox',
690 'label' => __('Taxable', 'easy-invoice'),
691 'container_class' => 'sm:col-span-6',
692 'default_value' => '1',
693 'order' => \EasyInvoice\Controllers\SettingsController::shouldShowInvoiceAdjustField() ? 7 : 6,
694 'description' => __('Check if this item is subject to tax.', 'easy-invoice'),
695 ];
696
697 return $fields;
698 }
699
700 /**
701 * Get client options for select field
702 *
703 * @since 1.0.0
704 * @return array
705 */
706 private function getClientOptions(): array {
707 $options = ['' => __('Select a client', 'easy-invoice')];
708
709 // Get clients from repository
710 $client_repository = new \EasyInvoice\Repositories\ClientRepository();
711 $clients = $client_repository->all();
712
713 foreach ($clients as $client) {
714 $client_name = $client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName());
715 $options[$client->getId()] = $client_name;
716 }
717
718 return $options;
719 }
720
721 /**
722 * Get the filter name for tabs
723 *
724 * @since 1.0.0
725 * @return string
726 */
727 protected function getTabFilterName(): string {
728 return 'easy_invoice_invoice_register_tab';
729 }
730
731 /**
732 * Get the filter name for fields
733 *
734 * @since 1.0.0
735 * @return string
736 */
737 protected function getFieldFilterName(): string {
738 return 'easy_invoice_invoice_register_field';
739 }
740
741 /**
742 * Get the filter name for templates
743 *
744 * @since 1.0.0
745 * @return string
746 */
747 protected function getTemplateFilterName(): string {
748 return 'easy_invoice_invoice_register_template';
749 }
750
751 /**
752 * Get the filter name for item fields
753 *
754 * @since 1.0.0
755 * @return string
756 */
757 protected function getItemFieldFilterName(): string {
758 return 'easy_invoice_invoice_register_item_field';
759 }
760 }