From 03b9defeb19eeb331b898d95d6c8bf72290c427f Mon Sep 17 00:00:00 2001 From: Martin Chevignard Date: Fri, 4 Oct 2024 12:07:29 +0200 Subject: [PATCH 1/4] Customers tax id field --- app/Http/Requests/CustomerRequest.php | 5 +++- .../Resources/Customer/CustomerResource.php | 1 + app/Http/Resources/CustomerResource.php | 1 + app/Traits/GeneratesPdfTrait.php | 1 + ...23_add_tax_id_field_to_customers_table.php | 28 +++++++++++++++++++ lang/en.json | 1 + package.json | 2 +- .../modal-components/CustomerModal.vue | 9 ++++++ .../scripts/admin/views/customers/Create.vue | 21 ++++++++++++++ .../components/base/BaseCustomInput.vue | 1 + resources/scripts/main.js | 5 ++-- vite.config.js | 9 ++---- 12 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 database/migrations/2024_10_04_093723_add_tax_id_field_to_customers_table.php diff --git a/app/Http/Requests/CustomerRequest.php b/app/Http/Requests/CustomerRequest.php index 58c58acd..d44ab492 100644 --- a/app/Http/Requests/CustomerRequest.php +++ b/app/Http/Requests/CustomerRequest.php @@ -49,8 +49,10 @@ class CustomerRequest extends FormRequest 'prefix' => [ 'nullable', ], + 'tax_id' => [ + 'nullable', + ], 'enable_portal' => [ - 'boolean', ], 'currency_id' => [ @@ -133,6 +135,7 @@ class CustomerRequest extends FormRequest 'password', 'phone', 'prefix', + 'tax_id', 'company_name', 'contact_name', 'website', diff --git a/app/Http/Resources/Customer/CustomerResource.php b/app/Http/Resources/Customer/CustomerResource.php index fe458620..72671de8 100644 --- a/app/Http/Resources/Customer/CustomerResource.php +++ b/app/Http/Resources/Customer/CustomerResource.php @@ -30,6 +30,7 @@ class CustomerResource extends JsonResource 'formatted_created_at' => $this->formattedCreatedAt, 'avatar' => $this->avatar, 'prefix' => $this->prefix, + 'tax_id' => $this->tax_id, 'billing' => $this->when($this->billingAddress()->exists(), function () { return new AddressResource($this->billingAddress); }), diff --git a/app/Http/Resources/CustomerResource.php b/app/Http/Resources/CustomerResource.php index 0430051b..b3f06da7 100644 --- a/app/Http/Resources/CustomerResource.php +++ b/app/Http/Resources/CustomerResource.php @@ -35,6 +35,7 @@ class CustomerResource extends JsonResource 'due_amount' => $this->due_amount, 'base_due_amount' => $this->base_due_amount, 'prefix' => $this->prefix, + 'tax_id' => $this->tax_id, 'billing' => $this->when($this->billingAddress()->exists(), function () { return new AddressResource($this->billingAddress); }), diff --git a/app/Traits/GeneratesPdfTrait.php b/app/Traits/GeneratesPdfTrait.php index 2de90f8a..eb900e73 100644 --- a/app/Traits/GeneratesPdfTrait.php +++ b/app/Traits/GeneratesPdfTrait.php @@ -145,6 +145,7 @@ trait GeneratesPdfTrait '{CONTACT_EMAIL}' => $customer->email, '{CONTACT_PHONE}' => $customer->phone, '{CONTACT_WEBSITE}' => $customer->website, + '{CONTACT_TAX_ID}' => __('pdf_tax_id').': '.$customer->tax_id, ]; $customFields = $this->fields; diff --git a/database/migrations/2024_10_04_093723_add_tax_id_field_to_customers_table.php b/database/migrations/2024_10_04_093723_add_tax_id_field_to_customers_table.php new file mode 100644 index 00000000..3a462b81 --- /dev/null +++ b/database/migrations/2024_10_04_093723_add_tax_id_field_to_customers_table.php @@ -0,0 +1,28 @@ +string('tax_id')->nullable()->after('github_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('customers', function (Blueprint $table) { + $table->dropColumn('tax_id'); + }); + } +}; diff --git a/lang/en.json b/lang/en.json index 9e042c29..7f9f0fb3 100644 --- a/lang/en.json +++ b/lang/en.json @@ -172,6 +172,7 @@ "customers": { "title": "Customers", "prefix": "Prefix", + "tax_id": "Tax ID", "add_customer": "Add Customer", "contacts_list": "Customer List", "name": "Name", diff --git a/package.json b/package.json index a06915b3..c08b2c1c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "type": "module", "scripts": { - "dev": "vite --host", + "dev": "vite", "build": "vite build", "serve": "vite preview", "test": "eslint ./resources/scripts --ext .js,.vue" diff --git a/resources/scripts/admin/components/modal-components/CustomerModal.vue b/resources/scripts/admin/components/modal-components/CustomerModal.vue index 14dbcf86..04e1c441 100644 --- a/resources/scripts/admin/components/modal-components/CustomerModal.vue +++ b/resources/scripts/admin/components/modal-components/CustomerModal.vue @@ -116,6 +116,15 @@ /> + + + + + diff --git a/resources/scripts/admin/views/customers/Create.vue b/resources/scripts/admin/views/customers/Create.vue index 8978f23e..77fa3f12 100644 --- a/resources/scripts/admin/views/customers/Create.vue +++ b/resources/scripts/admin/views/customers/Create.vue @@ -157,6 +157,24 @@ @input="v$.currentCustomer.prefix.$touch()" /> + + + + @@ -645,6 +663,9 @@ const rules = computed(() => { minLength(3) ), }, + tax_id: { + required: helpers.withMessage(t('validation.required'), required), + }, currency_id: { required: helpers.withMessage(t('validation.required'), required), }, diff --git a/resources/scripts/components/base/BaseCustomInput.vue b/resources/scripts/components/base/BaseCustomInput.vue index 3b35de80..e149ba10 100644 --- a/resources/scripts/components/base/BaseCustomInput.vue +++ b/resources/scripts/components/base/BaseCustomInput.vue @@ -180,6 +180,7 @@ async function getFields() { { label: 'Email', value: 'CONTACT_EMAIL' }, { label: 'Phone', value: 'CONTACT_PHONE' }, { label: 'Website', value: 'CONTACT_WEBSITE' }, + { label: 'Tax ID', value: 'CONTACT_TAX_ID' }, ...customerFields.value.map((i) => ({ label: i.label, value: i.slug, diff --git a/resources/scripts/main.js b/resources/scripts/main.js index e76e1629..ea02b1ad 100644 --- a/resources/scripts/main.js +++ b/resources/scripts/main.js @@ -14,11 +14,10 @@ import.meta.glob([ window.pinia = pinia window.Vuelidate = Vuelidate - -import InvoiceShelf from './InvoiceShelf' +import InvoiceShelf from './InvoiceShelf.js' window.Vue = Vue window.router = router window.VueRouter = VueRouter -window.InvoiceShelf = new InvoiceShelf() +window.InvoiceShelf = new InvoiceShelf() \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 8028526e..9a02b863 100644 --- a/vite.config.js +++ b/vite.config.js @@ -32,11 +32,8 @@ export default defineConfig({ }, }, }), - laravel({ - input: [ - 'resources/scripts/main.js', - ], - refresh: true, - }) + laravel([ + 'resources/scripts/main.js' + ]) ] }); From 168b741936ca416ed79dbf43a65bf4dfb8796b87 Mon Sep 17 00:00:00 2001 From: Martin Chevignard Date: Tue, 15 Oct 2024 16:20:04 +0200 Subject: [PATCH 2/4] Upadate filters with laravel best practices --- app/Models/Invoice.php | 70 ++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index ab8b3df1..ad2fa9db 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -250,51 +250,47 @@ class Invoice extends Model implements HasMedia { $filters = collect($filters); - if ($filters->get('search')) { - $query->whereSearch($filters->get('search')); - } + $query->when($filters->get('search'), function ($query, $search) { + $query->whereSearch($search); + }); - if ($filters->get('status')) { - if ( - $filters->get('status') == self::STATUS_UNPAID || - $filters->get('status') == self::STATUS_PARTIALLY_PAID || - $filters->get('status') == self::STATUS_PAID - ) { - $query->wherePaidStatus($filters->get('status')); - } elseif ($filters->get('status') == 'DUE') { - $query->whereDueStatus($filters->get('status')); - } else { - $query->whereStatus($filters->get('status')); - } - } + $query->when($filters->get('status'), function ($query, $status) { + match ($status) { + self::STATUS_UNPAID, self::STATUS_PARTIALLY_PAID, self::STATUS_PAID => $query->wherePaidStatus($status), + 'DUE' => $query->whereDueStatus($status), + default => $query->whereStatus($status), + }; + }); - if ($filters->get('paid_status')) { - $query->wherePaidStatus($filters->get('status')); - } + $query->when($filters->get('paid_status'), function ($query, $status) { + $query->wherePaidStatus($status); + }); - if ($filters->get('invoice_id')) { - $query->whereInvoice($filters->get('invoice_id')); - } + $query->when($filters->get('invoice_id'), function ($query, $invoiceId) { + $query->whereInvoice($invoiceId); + }); - if ($filters->get('invoice_number')) { - $query->whereInvoiceNumber($filters->get('invoice_number')); - } + $query->when($filters->get('invoice_number'), function ($query, $invoiceNumber) { + $query->whereInvoiceNumber($invoiceNumber); + }); - if ($filters->get('from_date') && $filters->get('to_date')) { - $start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date')); - $end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date')); + $query->when($filters->get('from_date') && $filters->get('to_date'), function ($query) use ($filters) { + $start = Carbon::parse($filters->get('from_date')); + $end = Carbon::parse($filters->get('to_date')); $query->invoicesBetween($start, $end); - } + }); - if ($filters->get('customer_id')) { - $query->whereCustomer($filters->get('customer_id')); - } + $query->when($filters->get('customer_id'), function ($query, $customerId) { + $query->where('customer_id', $customerId); + }); - if ($filters->get('orderByField') || $filters->get('orderBy')) { - $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'sequence_number'; - $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'desc'; - $query->whereOrder($field, $orderBy); - } + $query->when($filters->get('orderByField') || $filters->get('orderBy'), function ($query) use ($filters) { + $field = $filters->get('orderByField', 'sequence_number'); + $orderBy = $filters->get('orderBy', 'desc'); + $query->orderBy($field, $orderBy); + }); + + return $query; } public function scopeWhereInvoice($query, $invoice_id) diff --git a/package.json b/package.json index a06915b3..c08b2c1c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "type": "module", "scripts": { - "dev": "vite --host", + "dev": "vite", "build": "vite build", "serve": "vite preview", "test": "eslint ./resources/scripts --ext .js,.vue" From 59b43fa2584b90d531adb3b049cee043df552d49 Mon Sep 17 00:00:00 2001 From: Martin Chevignard Date: Tue, 15 Oct 2024 17:55:56 +0200 Subject: [PATCH 3/4] Public Invoice View fix --- .../V1/Admin/Invoice/InvoicesController.php | 5 +- app/Models/Invoice.php | 47 +++++++------------ .../scripts/admin/views/estimates/Index.vue | 4 ++ .../scripts/admin/views/invoices/Index.vue | 7 +++ .../components/base/base-table/BaseTable.vue | 17 ++++--- 5 files changed, 37 insertions(+), 43 deletions(-) diff --git a/app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php b/app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php index 91a186ef..0749ce48 100644 --- a/app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php +++ b/app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php @@ -21,12 +21,11 @@ class InvoicesController extends Controller { $this->authorize('viewAny', Invoice::class); - $limit = $request->has('limit') ? $request->limit : 10; + $limit = $request->input('limit', 10); $invoices = Invoice::whereCompany() - ->join('customers', 'customers.id', '=', 'invoices.customer_id') ->applyFilters($request->all()) - ->select('invoices.*', 'customers.name') + ->with('customer') ->latest() ->paginateData($limit); diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index ad2fa9db..1d9657d8 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -248,49 +248,34 @@ class Invoice extends Model implements HasMedia public function scopeApplyFilters($query, array $filters) { - $filters = collect($filters); + $filters = collect($filters)->filter()->all(); - $query->when($filters->get('search'), function ($query, $search) { + return $query->when($filters['search'] ?? null, function ($query, $search) { $query->whereSearch($search); - }); - - $query->when($filters->get('status'), function ($query, $status) { + })->when($filters['status'] ?? null, function ($query, $status) { match ($status) { self::STATUS_UNPAID, self::STATUS_PARTIALLY_PAID, self::STATUS_PAID => $query->wherePaidStatus($status), 'DUE' => $query->whereDueStatus($status), default => $query->whereStatus($status), }; - }); - - $query->when($filters->get('paid_status'), function ($query, $status) { - $query->wherePaidStatus($status); - }); - - $query->when($filters->get('invoice_id'), function ($query, $invoiceId) { + })->when($filters['paid_status'] ?? null, function ($query, $paidStatus) { + $query->wherePaidStatus($paidStatus); + })->when($filters['invoice_id'] ?? null, function ($query, $invoiceId) { $query->whereInvoice($invoiceId); - }); - - $query->when($filters->get('invoice_number'), function ($query, $invoiceNumber) { + })->when($filters['invoice_number'] ?? null, function ($query, $invoiceNumber) { $query->whereInvoiceNumber($invoiceNumber); - }); - - $query->when($filters->get('from_date') && $filters->get('to_date'), function ($query) use ($filters) { - $start = Carbon::parse($filters->get('from_date')); - $end = Carbon::parse($filters->get('to_date')); + })->when(($filters['from_date'] ?? null) && ($filters['to_date'] ?? null), function ($query) use ($filters) { + $start = Carbon::parse($filters['from_date']); + $end = Carbon::parse($filters['to_date']); $query->invoicesBetween($start, $end); - }); - - $query->when($filters->get('customer_id'), function ($query, $customerId) { + })->when($filters['customer_id'] ?? null, function ($query, $customerId) { $query->where('customer_id', $customerId); + })->when($filters['orderByField'] ?? null, function ($query, $orderByField) use ($filters) { + $orderBy = $filters['orderBy'] ?? 'desc'; + $query->orderBy($orderByField, $orderBy); + }, function ($query) { + $query->orderBy('sequence_number', 'desc'); }); - - $query->when($filters->get('orderByField') || $filters->get('orderBy'), function ($query) use ($filters) { - $field = $filters->get('orderByField', 'sequence_number'); - $orderBy = $filters->get('orderBy', 'desc'); - $query->orderBy($field, $orderBy); - }); - - return $query; } public function scopeWhereInvoice($query, $invoice_id) diff --git a/resources/scripts/admin/views/estimates/Index.vue b/resources/scripts/admin/views/estimates/Index.vue index 1ebe7dc0..8aebb666 100644 --- a/resources/scripts/admin/views/estimates/Index.vue +++ b/resources/scripts/admin/views/estimates/Index.vue @@ -174,6 +174,7 @@ :data="fetchData" :columns="estimateColumns" :placeholder-count="estimateStore.totalEstimateCount >= 20 ? 10 : 5" + :key="tableKey" class="mt-10" > +