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/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/Models/Invoice.php b/app/Models/Invoice.php
index ab8b3df1..1d9657d8 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -248,53 +248,34 @@ class Invoice extends Model implements HasMedia
public function scopeApplyFilters($query, array $filters)
{
- $filters = collect($filters);
+ $filters = collect($filters)->filter()->all();
- if ($filters->get('search')) {
- $query->whereSearch($filters->get('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'));
- }
- }
-
- if ($filters->get('paid_status')) {
- $query->wherePaidStatus($filters->get('status'));
- }
-
- if ($filters->get('invoice_id')) {
- $query->whereInvoice($filters->get('invoice_id'));
- }
-
- if ($filters->get('invoice_number')) {
- $query->whereInvoiceNumber($filters->get('invoice_number'));
- }
-
- 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'));
+ return $query->when($filters['search'] ?? null, function ($query, $search) {
+ $query->whereSearch($search);
+ })->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),
+ };
+ })->when($filters['paid_status'] ?? null, function ($query, $paidStatus) {
+ $query->wherePaidStatus($paidStatus);
+ })->when($filters['invoice_id'] ?? null, function ($query, $invoiceId) {
+ $query->whereInvoice($invoiceId);
+ })->when($filters['invoice_number'] ?? null, function ($query, $invoiceNumber) {
+ $query->whereInvoiceNumber($invoiceNumber);
+ })->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);
- }
-
- if ($filters->get('customer_id')) {
- $query->whereCustomer($filters->get('customer_id'));
- }
-
- 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);
- }
+ })->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');
+ });
}
public function scopeWhereInvoice($query, $invoice_id)
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 0c601b32..06522c13 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 30875827..ee999d51 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"
@@ -30,11 +30,12 @@
"@heroicons/vue": "^1.0.6",
"@popperjs/core": "^2.11.8",
"@stripe/stripe-js": "^2.4.0",
- "@tiptap/core": "^2.1.16",
- "@tiptap/extension-text-align": "^2.1.16",
- "@tiptap/pm": "^2.0.0",
- "@tiptap/starter-kit": "^2.1.16",
- "@tiptap/vue-3": "^2.1.16",
+ "@tiptap/core": "^2.8.0",
+ "@tiptap/extension-link": "^2.8.0",
+ "@tiptap/extension-text-align": "^2.8.0",
+ "@tiptap/pm": "^2.8.0",
+ "@tiptap/starter-kit": "^2.8.0",
+ "@tiptap/vue-3": "^2.8.0",
"@types/node": "^20.11.9",
"@vuelidate/components": "^1.2.6",
"@vuelidate/core": "^2.0.3",
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/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"
>
@@ -256,6 +257,7 @@ const dialogStore = useDialogStore()
const userStore = useUserStore()
const tableComponent = ref(null)
+const tableKey = ref(0)
const { t } = useI18n()
const showFilters = ref(false)
const status = ref([
@@ -408,6 +410,8 @@ function setFilters() {
state.selectAllField = false
})
+ tableKey.value += 1
+
refreshTable()
}
diff --git a/resources/scripts/admin/views/invoices/Index.vue b/resources/scripts/admin/views/invoices/Index.vue
index 5995abe2..ec5c6796 100644
--- a/resources/scripts/admin/views/invoices/Index.vue
+++ b/resources/scripts/admin/views/invoices/Index.vue
@@ -172,6 +172,7 @@
:data="fetchData"
:columns="invoiceColumns"
:placeholder-count="invoiceStore.invoiceTotalCount >= 20 ? 10 : 5"
+ :key="tableKey"
class="mt-10"
>
@@ -288,6 +289,7 @@ const { t } = useI18n()
// Local State
const utils = inject('$utils')
const table = ref(null)
+const tableKey = ref(0)
const showFilters = ref(false)
const status = ref([
@@ -416,9 +418,12 @@ async function fetchData({ page, filter, sort }) {
page,
}
+ console.log(data)
+
isRequestOngoing.value = true
let response = await invoiceStore.fetchInvoices(data)
+ console.log('API response:', response.data.data)
isRequestOngoing.value = false
@@ -464,6 +469,8 @@ function setFilters() {
state.selectAllField = false
})
+ tableKey.value += 1
+
refreshTable()
}
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/components/base/base-editor/BaseEditor.vue b/resources/scripts/components/base/base-editor/BaseEditor.vue
index 3a781a2d..b40f442d 100644
--- a/resources/scripts/components/base/base-editor/BaseEditor.vue
+++ b/resources/scripts/components/base/base-editor/BaseEditor.vue
@@ -8,598 +8,73 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- H1
-
-
- H2
-
-
- H3
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ {{ button.text }}
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- H1
-
-
- H2
-
-
- H3
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+