From d97d15f620f32444f497fdee1e769dc890cfb042 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 24 May 2023 21:42:43 +1000 Subject: [PATCH 01/12] fixes for tax calculations --- app/Helpers/Invoice/InvoiceItemSum.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index ff176ad6a762..68fb8cf541fe 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -170,7 +170,7 @@ class InvoiceItemSum private function shouldCalculateTax(): self { - if (!$this->invoice->company->calculate_taxes || !$this->invoice->company->account->isFreeHostedClient()) { + if (!$this->invoice->company->calculate_taxes || $this->invoice->company->account->isFreeHostedClient()) { $this->calc_tax = false; return $this; } From 214044675e9d8316b466e941c4f77c8b28476a30 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 24 May 2023 22:13:00 +1000 Subject: [PATCH 02/12] Updates for openapi spec --- app/Utils/Traits/CompanySettingsSaver.php | 10 +- openapi/api-docs.yaml | 363 +++++++++++++++++- openapi/components/schemas/invoice.yaml | 6 +- .../schemas/invoice_invitation.yaml | 57 +++ .../schemas/invoice_invitation_request.yaml | 59 +++ .../components/schemas/invoice_request.yaml | 237 ++++++++++++ openapi/paths/invoices.yaml | 2 +- 7 files changed, 725 insertions(+), 9 deletions(-) create mode 100644 openapi/components/schemas/invoice_invitation.yaml create mode 100644 openapi/components/schemas/invoice_invitation_request.yaml create mode 100644 openapi/components/schemas/invoice_request.yaml diff --git a/app/Utils/Traits/CompanySettingsSaver.php b/app/Utils/Traits/CompanySettingsSaver.php index 5bcd38bb4818..df4245b817c7 100644 --- a/app/Utils/Traits/CompanySettingsSaver.php +++ b/app/Utils/Traits/CompanySettingsSaver.php @@ -78,13 +78,13 @@ trait CompanySettingsSaver $entity->settings = $company_settings; - if(Ninja::isHosted() && array_key_exists('settings', $entity->getDirty())) + if(Ninja::isHosted() && $company_settings->country_id == "840" && array_key_exists('settings', $entity->getDirty())) { $old_settings = $entity->getOriginal()['settings']; - - if($settings->name != $old_settings->name) { - - nlog("name change {$old_settings->name} -> {$settings->name} "); + + /** Monitor changes of the Postal code */ + if($old_settings->postal_code != $company_settings->postal_code) + { } } diff --git a/openapi/api-docs.yaml b/openapi/api-docs.yaml index 1e96c319fc02..29784e0102a6 100644 --- a/openapi/api-docs.yaml +++ b/openapi/api-docs.yaml @@ -11940,7 +11940,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/FillableInvoice" + $ref: "#/components/schemas/InvoiceRequest" responses: 200: description: "Returns the saved invoice entity" @@ -15300,7 +15300,11 @@ components: description: 'An array of objects which define the line items of the invoice' items: $ref: '#/components/schemas/InvoiceItem' - + invitations: + type: array + description: 'An array of objects which define the invitations of the invoice' + items: + $ref: '#/components/schemas/InvoiceInvitation' amount: description: 'The invoice amount' type: number @@ -17089,6 +17093,66 @@ components: type: string example: google type: object + InvoiceInvitationRequest: + required: + - client_contact_id + properties: + id: + description: 'The entity invitation hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_contact_id: + description: 'The client contact hashed id' + type: string + example: Opnel5aKBz + key: + description: 'The invitation key' + type: string + example: Opnel5aKBz4343343566236gvbb + readOnly: true + link: + description: 'The invitation link' + type: string + example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' + readOnly: true + sent_date: + description: 'The invitation sent date' + type: string + format: date-time + readOnly: true + viewed_date: + description: 'The invitation viewed date' + type: string + format: date-time + readOnly: true + opened_date: + description: 'The invitation opened date' + type: string + format: date-time + readOnly: true + updated_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + email_error: + description: 'The email error' + type: string + example: 'The email error' + readOnly: true + email_status: + description: 'The email status' + type: string + readOnly: true + ClientRequest: required: - contacts @@ -18632,6 +18696,64 @@ components: format: integer example: '1434342123' type: object + InvoiceInvitation: + properties: + id: + description: 'The entity invitation hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_contact_id: + description: 'The client contact hashed id' + type: string + example: Opnel5aKBz + key: + description: 'The invitation key' + type: string + example: Opnel5aKBz4343343566236gvbb + readOnly: true + link: + description: 'The invitation link' + type: string + example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' + readOnly: true + sent_date: + description: 'The invitation sent date' + type: string + format: date-time + readOnly: true + viewed_date: + description: 'The invitation viewed date' + type: string + format: date-time + readOnly: true + opened_date: + description: 'The invitation opened date' + type: string + format: date-time + readOnly: true + updated_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + email_error: + description: 'The email error' + type: string + example: 'The email error' + readOnly: true + email_status: + description: 'The email status' + type: string + readOnly: true + RecurringQuote: properties: id: @@ -19249,6 +19371,243 @@ components: type: object + InvoiceRequest: + required: + - client_id + properties: + id: + description: 'The invoice hashed id' + type: string + example: Opnel5aKBz + readOnly: true + user_id: + description: 'The user hashed id' + type: string + example: Opnel5aKBz + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: Opnel5aKBz + company_id: + description: 'The company hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_id: + description: 'The client hashed id' + type: string + example: Opnel5aKBz + status_id: + description: 'The invoice status variable' + type: string + example: '4' + readOnly: true + number: + description: 'The invoice number - is a unique alpha numeric number per invoice per company' + type: string + example: INV_101 + po_number: + description: 'The purchase order associated with this invoice' + type: string + example: PO-1234 + terms: + description: 'The invoice terms' + type: string + example: 'These are invoice terms' + public_notes: + description: 'The public notes of the invoice' + type: string + example: 'These are some public notes' + private_notes: + description: 'The private notes of the invoice' + type: string + example: 'These are some private notes' + footer: + description: 'The invoice footer notes' + type: string + example: '' + custom_value1: + description: 'A custom field value' + type: string + example: '2022-10-01' + custom_value2: + description: 'A custom field value' + type: string + example: 'Something custom' + custom_value3: + description: 'A custom field value' + type: string + example: '' + custom_value4: + description: 'A custom field value' + type: string + example: '' + tax_name1: + description: 'The tax name' + type: string + example: '' + tax_name2: + description: 'The tax name' + type: string + example: '' + tax_rate1: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_rate2: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_name3: + description: 'The tax name' + type: string + example: '' + tax_rate3: + description: 'The tax rate' + type: number + format: float + example: '10.00' + total_taxes: + description: 'The total taxes for the invoice' + type: number + format: float + example: '10.00' + readOnly: + line_items: + type: array + description: 'An array of objects which define the line items of the invoice' + items: + $ref: '#/components/schemas/InvoiceItem' + invitations: + type: array + description: 'An array of objects which define the invitations of the invoice' + items: + $ref: '#/components/schemas/InvoiceInvitationRequest' + amount: + description: 'The invoice amount' + type: number + format: float + example: '10.00' + readOnly: true + balance: + description: 'The invoice balance' + type: number + format: float + example: '10.00' + readOnly: true + paid_to_date: + description: 'The amount paid on the invoice to date' + type: number + format: float + example: '10.00' + readOnly: true + discount: + description: 'The invoice discount, can be an amount or a percentage' + type: number + format: float + example: '10.00' + partial: + description: 'The deposit/partial amount' + type: number + format: float + example: '10.00' + is_amount_discount: + description: 'Flag determining if the discount is an amount or a percentage' + type: boolean + example: true + is_deleted: + description: 'Defines if the invoice has been deleted' + type: boolean + example: true + readOnly: true + uses_inclusive_taxes: + description: 'Defines the type of taxes used as either inclusive or exclusive' + type: boolean + example: true + date: + description: 'The Invoice Date' + type: string + format: date + example: '1994-07-30' + last_sent_date: + description: 'The last date the invoice was sent out' + type: string + format: date + example: '1994-07-30' + readOnly: true + next_send_date: + description: 'The Next date for a reminder to be sent' + type: string + format: date + example: '1994-07-30' + readOnly: true + partial_due_date: + description: 'The due date for the deposit/partial amount' + type: string + format: date + example: '1994-07-30' + due_date: + description: 'The due date of the invoice' + type: string + format: date + example: '1994-07-30' + last_viewed: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + updated_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + custom_surcharge1: + description: 'First Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge2: + description: 'Second Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge3: + description: 'Third Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge4: + description: 'Fourth Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge_tax1: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax2: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax3: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax4: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + type: object ClientContact: properties: id: diff --git a/openapi/components/schemas/invoice.yaml b/openapi/components/schemas/invoice.yaml index 6a09eb246788..cbb60434cfc5 100644 --- a/openapi/components/schemas/invoice.yaml +++ b/openapi/components/schemas/invoice.yaml @@ -101,7 +101,11 @@ description: 'An array of objects which define the line items of the invoice' items: $ref: '#/components/schemas/InvoiceItem' - + invitations: + type: array + description: 'An array of objects which define the invitations of the invoice' + items: + $ref: '#/components/schemas/InvoiceInvitation' amount: description: 'The invoice amount' type: number diff --git a/openapi/components/schemas/invoice_invitation.yaml b/openapi/components/schemas/invoice_invitation.yaml new file mode 100644 index 000000000000..ee39343f0c13 --- /dev/null +++ b/openapi/components/schemas/invoice_invitation.yaml @@ -0,0 +1,57 @@ + InvoiceInvitation: + properties: + id: + description: 'The entity invitation hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_contact_id: + description: 'The client contact hashed id' + type: string + example: Opnel5aKBz + key: + description: 'The invitation key' + type: string + example: Opnel5aKBz4343343566236gvbb + readOnly: true + link: + description: 'The invitation link' + type: string + example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' + readOnly: true + sent_date: + description: 'The invitation sent date' + type: string + format: date-time + readOnly: true + viewed_date: + description: 'The invitation viewed date' + type: string + format: date-time + readOnly: true + opened_date: + description: 'The invitation opened date' + type: string + format: date-time + readOnly: true + updated_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + email_error: + description: 'The email error' + type: string + example: 'The email error' + readOnly: true + email_status: + description: 'The email status' + type: string + readOnly: true diff --git a/openapi/components/schemas/invoice_invitation_request.yaml b/openapi/components/schemas/invoice_invitation_request.yaml new file mode 100644 index 000000000000..e3ecf5f390b0 --- /dev/null +++ b/openapi/components/schemas/invoice_invitation_request.yaml @@ -0,0 +1,59 @@ + InvoiceInvitationRequest: + required: + - client_contact_id + properties: + id: + description: 'The entity invitation hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_contact_id: + description: 'The client contact hashed id' + type: string + example: Opnel5aKBz + key: + description: 'The invitation key' + type: string + example: Opnel5aKBz4343343566236gvbb + readOnly: true + link: + description: 'The invitation link' + type: string + example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' + readOnly: true + sent_date: + description: 'The invitation sent date' + type: string + format: date-time + readOnly: true + viewed_date: + description: 'The invitation viewed date' + type: string + format: date-time + readOnly: true + opened_date: + description: 'The invitation opened date' + type: string + format: date-time + readOnly: true + updated_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + email_error: + description: 'The email error' + type: string + example: 'The email error' + readOnly: true + email_status: + description: 'The email status' + type: string + readOnly: true diff --git a/openapi/components/schemas/invoice_request.yaml b/openapi/components/schemas/invoice_request.yaml new file mode 100644 index 000000000000..1043f1b65060 --- /dev/null +++ b/openapi/components/schemas/invoice_request.yaml @@ -0,0 +1,237 @@ + InvoiceRequest: + required: + - client_id + properties: + id: + description: 'The invoice hashed id' + type: string + example: Opnel5aKBz + readOnly: true + user_id: + description: 'The user hashed id' + type: string + example: Opnel5aKBz + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: Opnel5aKBz + company_id: + description: 'The company hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_id: + description: 'The client hashed id' + type: string + example: Opnel5aKBz + status_id: + description: 'The invoice status variable' + type: string + example: '4' + readOnly: true + number: + description: 'The invoice number - is a unique alpha numeric number per invoice per company' + type: string + example: INV_101 + po_number: + description: 'The purchase order associated with this invoice' + type: string + example: PO-1234 + terms: + description: 'The invoice terms' + type: string + example: 'These are invoice terms' + public_notes: + description: 'The public notes of the invoice' + type: string + example: 'These are some public notes' + private_notes: + description: 'The private notes of the invoice' + type: string + example: 'These are some private notes' + footer: + description: 'The invoice footer notes' + type: string + example: '' + custom_value1: + description: 'A custom field value' + type: string + example: '2022-10-01' + custom_value2: + description: 'A custom field value' + type: string + example: 'Something custom' + custom_value3: + description: 'A custom field value' + type: string + example: '' + custom_value4: + description: 'A custom field value' + type: string + example: '' + tax_name1: + description: 'The tax name' + type: string + example: '' + tax_name2: + description: 'The tax name' + type: string + example: '' + tax_rate1: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_rate2: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_name3: + description: 'The tax name' + type: string + example: '' + tax_rate3: + description: 'The tax rate' + type: number + format: float + example: '10.00' + total_taxes: + description: 'The total taxes for the invoice' + type: number + format: float + example: '10.00' + readOnly: + line_items: + type: array + description: 'An array of objects which define the line items of the invoice' + items: + $ref: '#/components/schemas/InvoiceItem' + invitations: + type: array + description: 'An array of objects which define the invitations of the invoice' + items: + $ref: '#/components/schemas/InvoiceInvitationRequest' + amount: + description: 'The invoice amount' + type: number + format: float + example: '10.00' + readOnly: true + balance: + description: 'The invoice balance' + type: number + format: float + example: '10.00' + readOnly: true + paid_to_date: + description: 'The amount paid on the invoice to date' + type: number + format: float + example: '10.00' + readOnly: true + discount: + description: 'The invoice discount, can be an amount or a percentage' + type: number + format: float + example: '10.00' + partial: + description: 'The deposit/partial amount' + type: number + format: float + example: '10.00' + is_amount_discount: + description: 'Flag determining if the discount is an amount or a percentage' + type: boolean + example: true + is_deleted: + description: 'Defines if the invoice has been deleted' + type: boolean + example: true + readOnly: true + uses_inclusive_taxes: + description: 'Defines the type of taxes used as either inclusive or exclusive' + type: boolean + example: true + date: + description: 'The Invoice Date' + type: string + format: date + example: '1994-07-30' + last_sent_date: + description: 'The last date the invoice was sent out' + type: string + format: date + example: '1994-07-30' + readOnly: true + next_send_date: + description: 'The Next date for a reminder to be sent' + type: string + format: date + example: '1994-07-30' + readOnly: true + partial_due_date: + description: 'The due date for the deposit/partial amount' + type: string + format: date + example: '1994-07-30' + due_date: + description: 'The due date of the invoice' + type: string + format: date + example: '1994-07-30' + last_viewed: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + updated_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + custom_surcharge1: + description: 'First Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge2: + description: 'Second Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge3: + description: 'Third Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge4: + description: 'Fourth Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge_tax1: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax2: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax3: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax4: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + type: object \ No newline at end of file diff --git a/openapi/paths/invoices.yaml b/openapi/paths/invoices.yaml index 201098c6335d..13e473c47fda 100644 --- a/openapi/paths/invoices.yaml +++ b/openapi/paths/invoices.yaml @@ -135,7 +135,7 @@ content: application/json: schema: - $ref: "#/components/schemas/FillableInvoice" + $ref: "#/components/schemas/InvoiceRequest" responses: 200: description: "Returns the saved invoice entity" From ee7ab54b389417939993f76fb822b98fdaa0d4d1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 24 May 2023 22:26:10 +1000 Subject: [PATCH 03/12] Update company tax data --- app/Jobs/Company/CompanyTaxRate.php | 45 +++++++++++++++++++++++ app/Utils/Traits/CompanySettingsSaver.php | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/Jobs/Company/CompanyTaxRate.php diff --git a/app/Jobs/Company/CompanyTaxRate.php b/app/Jobs/Company/CompanyTaxRate.php new file mode 100644 index 000000000000..1abb6c848ab5 --- /dev/null +++ b/app/Jobs/Company/CompanyTaxRate.php @@ -0,0 +1,45 @@ +company->db); + + $tp = new TaxProvider($this->company); + $tp->updateCompanyTaxData(); + + } + +} \ No newline at end of file diff --git a/app/Utils/Traits/CompanySettingsSaver.php b/app/Utils/Traits/CompanySettingsSaver.php index df4245b817c7..0a351767659e 100644 --- a/app/Utils/Traits/CompanySettingsSaver.php +++ b/app/Utils/Traits/CompanySettingsSaver.php @@ -78,7 +78,7 @@ trait CompanySettingsSaver $entity->settings = $company_settings; - if(Ninja::isHosted() && $company_settings->country_id == "840" && array_key_exists('settings', $entity->getDirty())) + if(Ninja::isHosted() && $entity?->calc_taxes && $company_settings->country_id == "840" && array_key_exists('settings', $entity->getDirty())) { $old_settings = $entity->getOriginal()['settings']; From 32daee0fa67eee455fe86ebabd0e9b039842ee42 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 24 May 2023 22:40:40 +1000 Subject: [PATCH 04/12] Update company tax data --- app/DataMapper/Tax/BaseRule.php | 13 +++++++------ app/Jobs/Client/UpdateTaxData.php | 10 ++++++++-- app/Jobs/Company/CompanyTaxRate.php | 9 +++++++++ app/Utils/Traits/CompanySettingsSaver.php | 13 +++++++++++-- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 1a89d4ea5718..7fba1ff96c91 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -184,14 +184,15 @@ class BaseRule implements RuleInterface $company = $this->invoice->company; /** If no company tax data has been configured, lets do that now. */ - if(!$company->origin_tax_data && \DB::transactionLevel() == 0) - { + /** We should never encounter this scenario */ + // if(!$company->origin_tax_data && \DB::transactionLevel() == 0) + // { - $tp = new TaxProvider($company); - $tp->updateCompanyTaxData(); - $company->fresh(); + // $tp = new TaxProvider($company); + // $tp->updateCompanyTaxData(); + // $company->fresh(); - } + // } /** If we are in a Origin based state, force the company tax here */ if($company->origin_tax_data?->originDestination == 'O' && ($company->tax_data?->seller_subregion == $this->client_subregion)) { diff --git a/app/Jobs/Client/UpdateTaxData.php b/app/Jobs/Client/UpdateTaxData.php index 1f640f9a0b34..ec34e7b62b51 100644 --- a/app/Jobs/Client/UpdateTaxData.php +++ b/app/Jobs/Client/UpdateTaxData.php @@ -11,16 +11,17 @@ namespace App\Jobs\Client; -use App\DataProviders\USStates; use App\Models\Client; use App\Models\Company; use App\Libraries\MultiDB; use Illuminate\Bus\Queueable; +use App\DataProviders\USStates; use App\Utils\Traits\MakesHash; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\Middleware\WithoutOverlapping; class UpdateTaxData implements ShouldQueue { @@ -73,6 +74,11 @@ class UpdateTaxData implements ShouldQueue nlog("problem getting tax data => ".$e->getMessage()); } - } + + public function middleware() + { + return [new WithoutOverlapping($this->company->id)]; + } + } \ No newline at end of file diff --git a/app/Jobs/Company/CompanyTaxRate.php b/app/Jobs/Company/CompanyTaxRate.php index 1abb6c848ab5..0ca0d813fa4b 100644 --- a/app/Jobs/Company/CompanyTaxRate.php +++ b/app/Jobs/Company/CompanyTaxRate.php @@ -19,11 +19,14 @@ use Illuminate\Queue\InteractsWithQueue; use App\Services\Tax\Providers\TaxProvider; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Queue\Middleware\WithoutOverlapping; class CompanyTaxRate implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + public $tries = 1; + /** * Create a new job instance. * @@ -38,8 +41,14 @@ class CompanyTaxRate implements ShouldQueue MultiDB::setDB($this->company->db); $tp = new TaxProvider($this->company); + $tp->updateCompanyTaxData(); } + public function middleware() + { + return [new WithoutOverlapping($this->company->id)]; + } + } \ No newline at end of file diff --git a/app/Utils/Traits/CompanySettingsSaver.php b/app/Utils/Traits/CompanySettingsSaver.php index 0a351767659e..79d08059fc20 100644 --- a/app/Utils/Traits/CompanySettingsSaver.php +++ b/app/Utils/Traits/CompanySettingsSaver.php @@ -15,6 +15,7 @@ use stdClass; use App\Utils\Ninja; use App\Models\Company; use App\DataMapper\CompanySettings; +use App\Jobs\Company\CompanyTaxRate; /** * Class CompanySettingsSaver. @@ -78,15 +79,23 @@ trait CompanySettingsSaver $entity->settings = $company_settings; - if(Ninja::isHosted() && $entity?->calc_taxes && $company_settings->country_id == "840" && array_key_exists('settings', $entity->getDirty())) + if( $entity?->calculate_taxes && $company_settings->country_id == "840" && array_key_exists('settings', $entity->getDirty())) { $old_settings = $entity->getOriginal()['settings']; /** Monitor changes of the Postal code */ if($old_settings->postal_code != $company_settings->postal_code) { - + nlog("postal code change"); + CompanyTaxRate::dispatch($entity); } + + } + elseif( $entity?->calculate_taxes && $company_settings->country_id == "840" && array_key_exists('calculate_taxes', $entity->getDirty()) && $entity->getOriginal('calculate_taxes') == 0) + { + nlog("calc taxes change"); + nlog($entity->getOriginal('calculate_taxes')); + CompanyTaxRate::dispatch($entity); } From 36449fa56c70c0cbfc3e013663501301f6df0bcf Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 24 May 2023 23:06:27 +1000 Subject: [PATCH 05/12] Update company tax data --- app/DataMapper/Tax/BaseRule.php | 39 +++++++++++++++----------- app/Helpers/Invoice/InvoiceItemSum.php | 2 +- app/Jobs/Client/UpdateTaxData.php | 1 - app/Jobs/Company/CompanyTaxRate.php | 25 ++++++++++++++++- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 7fba1ff96c91..c30f8a5d7a21 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -119,6 +119,8 @@ class BaseRule implements RuleInterface public mixed $invoice; + private bool $should_calc_tax = true; + public function __construct() { } @@ -128,6 +130,10 @@ class BaseRule implements RuleInterface return $this; } + public function shouldCalcTax(): bool + { + return $this->should_calc_tax; + } /** * Initializes the tax rule for the entity. * @@ -185,17 +191,16 @@ class BaseRule implements RuleInterface /** If no company tax data has been configured, lets do that now. */ /** We should never encounter this scenario */ - // if(!$company->origin_tax_data && \DB::transactionLevel() == 0) - // { - - // $tp = new TaxProvider($company); - // $tp->updateCompanyTaxData(); - // $company->fresh(); - - // } + if(!$company->origin_tax_data) + { + $this->should_calc_tax = false; + + return $this; + + } /** If we are in a Origin based state, force the company tax here */ - if($company->origin_tax_data?->originDestination == 'O' && ($company->tax_data?->seller_subregion == $this->client_subregion)) { + if($company?->origin_tax_data?->originDestination == 'O' && ($company->tax_data?->seller_subregion == $this->client_subregion)) { $tax_data = $company->origin_tax_data; @@ -203,14 +208,14 @@ class BaseRule implements RuleInterface else{ /** Ensures the client tax data has been updated */ - if(!$this->client->tax_data && \DB::transactionLevel() == 0) { + // if(!$this->client->tax_data && \DB::transactionLevel() == 0) { - $tp = new TaxProvider($company, $this->client); - $tp->updateClientTaxData(); - $this->client->fresh(); - } - - $tax_data = $this->client->tax_data; + // $tp = new TaxProvider($company, $this->client); + // $tp->updateClientTaxData(); + // $this->client->fresh(); + // } + if($this->client->tax_data) + $tax_data = $this->client->tax_data; } @@ -219,7 +224,7 @@ class BaseRule implements RuleInterface /** Applies the tax data to the invoice */ if($this->invoice instanceof Invoice && $tax_data) { - $this->invoice->tax_data = $tax_data ; + $this->invoice->tax_data = $tax_data; if(\DB::transactionLevel() == 0) $this->invoice->saveQuietly(); diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 68fb8cf541fe..62999672eb0e 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -188,7 +188,7 @@ class InvoiceItemSum ->setEntity($this->invoice) ->init(); - $this->calc_tax = true; + $this->calc_tax = $this->rule->shouldCalcTax(); return $this; } diff --git a/app/Jobs/Client/UpdateTaxData.php b/app/Jobs/Client/UpdateTaxData.php index ec34e7b62b51..314fbff04d24 100644 --- a/app/Jobs/Client/UpdateTaxData.php +++ b/app/Jobs/Client/UpdateTaxData.php @@ -60,7 +60,6 @@ class UpdateTaxData implements ShouldQueue $tax_provider->updateClientTaxData(); - if (!$this->client->state && $this->client->postal_code) { $this->client->state = USStates::getState($this->client->postal_code); diff --git a/app/Jobs/Company/CompanyTaxRate.php b/app/Jobs/Company/CompanyTaxRate.php index 0ca0d813fa4b..9615cdbbf8a4 100644 --- a/app/Jobs/Company/CompanyTaxRate.php +++ b/app/Jobs/Company/CompanyTaxRate.php @@ -11,9 +11,11 @@ namespace App\Jobs\Company; +use App\Models\Client; use App\Models\Company; use App\Libraries\MultiDB; use Illuminate\Bus\Queueable; +use App\Jobs\Client\UpdateTaxData; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use App\Services\Tax\Providers\TaxProvider; @@ -38,11 +40,32 @@ class CompanyTaxRate implements ShouldQueue public function handle() { + + if(!config('services.tax.zip_tax.key')) { + return; + } + MultiDB::setDB($this->company->db); $tp = new TaxProvider($this->company); - + $tp->updateCompanyTaxData(); + + $tp = null; + + Client::query() + ->where('company_id', $this->company->id) + ->where('is_deleted', false) + ->where('country_id', 840) + ->whereNotNull('postal_code') + ->whereNull('tax_data') + ->whereFalse('is_tax_exempt') + ->cursor() + ->each(function ($client) { + + (new UpdateTaxData($client, $this->company))->handle(); + + }); } From e5c3c1b257f2b661723966d27b3282e185962c9f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 24 May 2023 23:31:19 +1000 Subject: [PATCH 06/12] FIxes for the base rule --- app/DataMapper/Tax/BaseRule.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index c30f8a5d7a21..b5be906b2328 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -182,7 +182,7 @@ class BaseRule implements RuleInterface * Destination - Client Tax Data * */ - // $tax_data = new Response([]); + $tax_data = false; if($this->seller_region == 'US' && $this->client_region == 'US'){ @@ -193,14 +193,12 @@ class BaseRule implements RuleInterface /** We should never encounter this scenario */ if(!$company->origin_tax_data) { - $this->should_calc_tax = false; - + $this->should_calc_tax = false; return $this; - } /** If we are in a Origin based state, force the company tax here */ - if($company?->origin_tax_data?->originDestination == 'O' && ($company->tax_data?->seller_subregion == $this->client_subregion)) { + if($company->origin_tax_data?->originDestination == 'O' && ($company->tax_data?->seller_subregion == $this->client_subregion)) { $tax_data = $company->origin_tax_data; From 524901c8721232d3601b1c98ba45fede63a554d0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 25 May 2023 07:25:17 +1000 Subject: [PATCH 07/12] Add expense categories if they do not exist on import --- app/Import/Transformer/BaseTransformer.php | 9 +++++++++ app/Jobs/Company/CompanyTaxRate.php | 2 +- app/Models/ExpenseCategory.php | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Import/Transformer/BaseTransformer.php b/app/Import/Transformer/BaseTransformer.php index 954f668d3b18..2693651cde5d 100644 --- a/app/Import/Transformer/BaseTransformer.php +++ b/app/Import/Transformer/BaseTransformer.php @@ -600,6 +600,8 @@ class BaseTransformer */ public function getExpenseCategoryId($name) { + /** @var \App\Models\ExpenseCategory $ec */ + $ec = ExpenseCategory::where('company_id', $this->company->id) ->where('is_deleted', false) ->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [ @@ -607,6 +609,13 @@ class BaseTransformer ]) ->first(); + if($ec) + return $ec->id; + + $ec = \App\Factory\ExpenseCategoryFactory::create($this->company->id, $this->company->owner()->id); + $ec->name = $name; + $ec->save(); + return $ec ? $ec->id : null; } diff --git a/app/Jobs/Company/CompanyTaxRate.php b/app/Jobs/Company/CompanyTaxRate.php index 9615cdbbf8a4..7da21fb92e24 100644 --- a/app/Jobs/Company/CompanyTaxRate.php +++ b/app/Jobs/Company/CompanyTaxRate.php @@ -59,7 +59,7 @@ class CompanyTaxRate implements ShouldQueue ->where('country_id', 840) ->whereNotNull('postal_code') ->whereNull('tax_data') - ->whereFalse('is_tax_exempt') + ->where('is_tax_exempt', false) ->cursor() ->each(function ($client) { diff --git a/app/Models/ExpenseCategory.php b/app/Models/ExpenseCategory.php index 517abfbf3b2c..6dd525b5c0dd 100644 --- a/app/Models/ExpenseCategory.php +++ b/app/Models/ExpenseCategory.php @@ -24,7 +24,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property int|null $created_at * @property int|null $updated_at * @property int|null $deleted_at - * @property int $is_deleted + * @property bool $is_deleted * @property string $color * @property int|null $bank_category_id * @property-read \App\Models\Expense|null $expense From 527c99eb5b2ce98476bf7b9b267c38de085dbc5f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 25 May 2023 10:38:43 +1000 Subject: [PATCH 08/12] Adjustments for taxes --- app/DataMapper/Tax/BaseRule.php | 1 + app/DataMapper/Tax/US/Rule.php | 6 ++- app/Services/Tax/Providers/TaxProvider.php | 62 +++++++++++++++++++--- 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index b5be906b2328..55666506218c 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -212,6 +212,7 @@ class BaseRule implements RuleInterface // $tp->updateClientTaxData(); // $this->client->fresh(); // } + if($this->client->tax_data) $tax_data = $this->client->tax_data; diff --git a/app/DataMapper/Tax/US/Rule.php b/app/DataMapper/Tax/US/Rule.php index 6749ad162a0c..600dcf2b0fc8 100644 --- a/app/DataMapper/Tax/US/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -161,13 +161,15 @@ class Rule extends BaseRule implements RuleInterface if($this->tax_data?->stateSalesTax == 0) { $this->tax_rate1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate; - $this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name; + $this->tax_name1 = "Sales Tax"; + // $this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name; return $this; } $this->tax_rate1 = $this->tax_data->taxSales * 100; - $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; + // $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; + $this->tax_name1 = "Sales Tax"; return $this; } diff --git a/app/Services/Tax/Providers/TaxProvider.php b/app/Services/Tax/Providers/TaxProvider.php index ff92d455ed65..ccc5489f71f0 100644 --- a/app/Services/Tax/Providers/TaxProvider.php +++ b/app/Services/Tax/Providers/TaxProvider.php @@ -56,7 +56,12 @@ class TaxProvider { } - + + /** + * updateCompanyTaxData + * + * @return self + */ public function updateCompanyTaxData(): self { $this->configureProvider($this->provider, $this->company->country()->iso_3166_2); //hard coded for now to one provider, but we'll be able to swap these out later @@ -83,7 +88,12 @@ class TaxProvider return $this; } - + + /** + * updateClientTaxData + * + * @return self + */ public function updateClientTaxData(): self { $this->configureProvider($this->provider, $this->client->country->iso_3166_2); //hard coded for now to one provider, but we'll be able to swap these out later @@ -106,8 +116,9 @@ class TaxProvider 'country_id' => $this->client->shipping_country_id, ]; - - $tax_provider = new $this->provider($billing_details); + $taxable_address = $this->taxShippingAddress() ? $shipping_details : $billing_details; + + $tax_provider = new $this->provider($taxable_address); $tax_provider->setApiCredentials($this->api_credentials); @@ -120,7 +131,29 @@ class TaxProvider return $this; } + + /** + * taxShippingAddress + * + * @return bool + */ + private function taxShippingAddress(): bool + { + + if($this->client->shipping_country_id == "840" && strlen($this->client->shipping_postal_code) > 3) + return true; + return false; + + } + + /** + * configureProvider + * + * @param string $provider + * @param string $country_code + * @return self + */ private function configureProvider(?string $provider, string $country_code): self { @@ -159,21 +192,36 @@ class TaxProvider return $this; } - + + /** + * configureEuTax + * + * @return self + */ private function configureEuTax(): self { $this->provider = EuTax::class; return $this; } - + + /** + * noTaxRegionDefined + * + * @return void + */ private function noTaxRegionDefined() { throw new \Exception("No tax region defined for this country"); // return $this; } - + + /** + * configureZipTax + * + * @return self + */ private function configureZipTax(): self { From 3e0f821b65bb7437fd178b2cebb3dffd5ec9a98b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 25 May 2023 11:32:17 +1000 Subject: [PATCH 09/12] Allow single transactions with Auth.net --- .../Authorize/AuthorizeCreditCard.php | 42 +++-- .../Authorize/AuthorizeTransaction.php | 154 ++++++++++++++++++ 2 files changed, 179 insertions(+), 17 deletions(-) create mode 100644 app/PaymentDrivers/Authorize/AuthorizeTransaction.php diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php index c49a7831b64a..e1cbb863ce40 100644 --- a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php +++ b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php @@ -12,20 +12,21 @@ namespace App\PaymentDrivers\Authorize; -use App\Exceptions\PaymentFailed; -use App\Jobs\Util\SystemLogger; -use App\Models\ClientGatewayToken; -use App\Models\GatewayType; use App\Models\Payment; +use App\Models\SystemLog; +use App\Models\GatewayType; use App\Models\PaymentHash; use App\Models\PaymentType; -use App\Models\SystemLog; -use App\PaymentDrivers\AuthorizePaymentDriver; +use App\Jobs\Util\SystemLogger; use App\Utils\Traits\MakesHash; -use net\authorize\api\contract\v1\DeleteCustomerPaymentProfileRequest; +use App\Exceptions\PaymentFailed; +use App\Models\ClientGatewayToken; +use App\PaymentDrivers\AuthorizePaymentDriver; +use App\PaymentDrivers\Authorize\AuthorizeTransaction; use net\authorize\api\contract\v1\DeleteCustomerProfileRequest; -use net\authorize\api\controller\DeleteCustomerPaymentProfileController; use net\authorize\api\controller\DeleteCustomerProfileController; +use net\authorize\api\contract\v1\DeleteCustomerPaymentProfileRequest; +use net\authorize\api\controller\DeleteCustomerPaymentProfileController; /** * Class AuthorizeCreditCard. @@ -68,19 +69,26 @@ class AuthorizeCreditCard $gateway_customer_reference = $authorise_create_customer->create($data); - $authorise_payment_method = new AuthorizePaymentMethod($this->authorize); - - $payment_profile = $authorise_payment_method->addPaymentMethodToClient($gateway_customer_reference, $data); - $payment_profile_id = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId(); - - $data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($gateway_customer_reference, $payment_profile_id, $data['amount_with_fee']); - if ($request->has('store_card') && $request->input('store_card') === true) { + + $authorise_payment_method = new AuthorizePaymentMethod($this->authorize); + + $payment_profile = $authorise_payment_method->addPaymentMethodToClient($gateway_customer_reference, $data); + $payment_profile_id = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId(); + + $data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($gateway_customer_reference, $payment_profile_id, $data['amount_with_fee']); + $authorise_payment_method->payment_method = GatewayType::CREDIT_CARD; $client_gateway_token = $authorise_payment_method->createClientGatewayToken($payment_profile, $gateway_customer_reference); + } else { - //remove the payment profile if we are not storing tokens in our system - $this->removePaymentProfile($gateway_customer_reference, $payment_profile_id); + + $authorise_transaction = new AuthorizeTransaction($this->authorize); + $data = $authorise_transaction->chargeCustomer($gateway_customer_reference, $data); + + $transaction_id = $data['transaction_id']; + nlog($transaction_id); + } return $this->handleResponse($data, $request); diff --git a/app/PaymentDrivers/Authorize/AuthorizeTransaction.php b/app/PaymentDrivers/Authorize/AuthorizeTransaction.php new file mode 100644 index 000000000000..d94eac575470 --- /dev/null +++ b/app/PaymentDrivers/Authorize/AuthorizeTransaction.php @@ -0,0 +1,154 @@ +authorize = $authorize; + } + + public function chargeCustomer(string $profile_id, array $data) + { + $this->authorize->init(); + + // Set the transaction's refId + $refId = 'ref'.time(); + + $op = new OpaqueDataType(); + $op->setDataDescriptor($data['dataDescriptor']); + $op->setDataValue($data['dataValue']); + $paymentOne = new PaymentType(); + $paymentOne->setOpaqueData($op); + $amount = $data['amount_with_fee']; + + $invoice_numbers = ''; + $po_numbers = ''; + $taxAmount = 0; + $invoiceTotal = 0; + $invoiceTaxes = 0; + + if ($this->authorize->payment_hash->data) { + $invoice_numbers = collect($this->authorize->payment_hash->data->invoices)->pluck('invoice_number')->implode(','); + $invObj = Invoice::whereIn('id', $this->transformKeys(array_column($this->authorize->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get(); + + $po_numbers = $invObj->pluck('po_number')->implode(','); + + $invoiceTotal = round($invObj->pluck('amount')->sum(), 2); + $invoiceTaxes = round($invObj->pluck('total_taxes')->sum(), 2); + + if ($invoiceTotal != $amount) { + $taxRatio = $amount / $invoiceTotal; + $taxAmount = round($invoiceTaxes * $taxRatio, 2); + } else { + $taxAmount = $invoiceTaxes; + } + } + + $description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->authorize->client->present()->name()}"; + + $order = new OrderType(); + $order->setInvoiceNumber(substr($invoice_numbers, 0, 19)); + $order->setDescription(substr($description, 0, 255)); + $order->setSupplierOrderReference(substr($po_numbers, 0, 19));// 04-03-2023 + + $tax = new ExtendedAmountType(); + $tax->setName('tax'); + $tax->setAmount($taxAmount); + + // Add values for transaction settings + $duplicateWindowSetting = new SettingType(); + $duplicateWindowSetting->setSettingName("duplicateWindow"); + $duplicateWindowSetting->setSettingValue("60"); + + $transactionRequestType = new TransactionRequestType(); + $transactionRequestType->setTransactionType('authCaptureTransaction'); + $transactionRequestType->setAmount($amount); + $transactionRequestType->setTax($tax); + $transactionRequestType->setTaxExempt(empty($taxAmount)); + $transactionRequestType->setOrder($order); + $transactionRequestType->addToTransactionSettings($duplicateWindowSetting); + + $transactionRequestType->setPayment($paymentOne); + // $transactionRequestType->setProfile($profileToCharge); + $transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code); + + $request = new CreateTransactionRequest(); + $request->setMerchantAuthentication($this->authorize->merchant_authentication); + $request->setRefId($refId); + $request->setTransactionRequest($transactionRequestType); + $controller = new CreateTransactionController($request); + $response = $controller->executeWithApiResponse($this->authorize->mode()); + + if ($response != null && $response->getMessages()->getResultCode() == 'Ok') { + $tresponse = $response->getTransactionResponse(); + + if ($tresponse != null && $tresponse->getMessages() != null) { + nlog(' Transaction Response code : '.$tresponse->getResponseCode()); + nlog(' Charge Customer Profile APPROVED :'); + nlog(' Charge Customer Profile AUTH CODE : '.$tresponse->getAuthCode()); + nlog(' Charge Customer Profile TRANS ID : '.$tresponse->getTransId()); + nlog(' Code : '.$tresponse->getMessages()[0]->getCode()); + nlog(' Description : '.$tresponse->getMessages()[0]->getDescription()); + nlog(print_r($tresponse->getMessages()[0], 1)); + } else { + nlog('Transaction Failed '); + if ($tresponse->getErrors() != null) { + nlog(' Error code : '.$tresponse->getErrors()[0]->getErrorCode()); + nlog(' Error message : '.$tresponse->getErrors()[0]->getErrorText()); + nlog(print_r($tresponse->getErrors()[0], 1)); + } + } + } else { + nlog('Transaction Failed '); + $tresponse = $response->getTransactionResponse(); + if ($tresponse != null && $tresponse->getErrors() != null) { + nlog(' Error code : '.$tresponse->getErrors()[0]->getErrorCode()); + nlog(' Error message : '.$tresponse->getErrors()[0]->getErrorText()); + nlog(print_r($tresponse->getErrors()[0], 1)); + } else { + nlog(' Error code : '.$response->getMessages()->getMessage()[0]->getCode()); + nlog(' Error message : '.$response->getMessages()->getMessage()[0]->getText()); + } + } + + return [ + 'response' => $tresponse, + 'amount' => $amount, + 'profile_id' => $profile_id, + 'transaction_id' => $tresponse->getTransId() + // 'payment_profile_id' => $payment_profile_id, + ]; + } + +} From ce8fc061ed442907c98f84260993b19b2463f198 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 25 May 2023 12:07:00 +1000 Subject: [PATCH 10/12] Fixes for tests --- tests/Unit/Tax/SumTaxTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index c57add9afac4..b30b3d1ca8cf 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -200,7 +200,7 @@ class SumTaxTest extends TestCase $line_items = $invoice->line_items; $this->assertEquals(10.88, $invoice->amount); - $this->assertEquals("CA Sales Tax", $line_items[0]->tax_name1); + $this->assertEquals("Sales Tax", $line_items[0]->tax_name1); $this->assertEquals(8.75, $line_items[0]->tax_rate1); } From 3e07acb5bb8e67ce3d7b0b68f3c0f3961e0b44d3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 25 May 2023 13:35:06 +1000 Subject: [PATCH 11/12] Fixes for signing certificates --- app/Models/Company.php | 14 ++++++++++++++ app/Services/Invoice/EInvoice/FacturaEInvoice.php | 13 ++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/Models/Company.php b/app/Models/Company.php index c0bb8f2df705..b062d01d4b46 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -904,4 +904,18 @@ class Company extends BaseModel return $item->id == $this->getSetting('date_format_id'); })->first()->format; } + + public function getInvoiceCert() + { + if($this->e_invoice_certificate) + return base64_decode($this->e_invoice_certificate); + + return false; + } + + public function getSslPassPhrase() + { + return $this->e_invoice_certificate_passphrase; + } + } diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/Invoice/EInvoice/FacturaEInvoice.php index 98802616ac9e..ffb9622b2670 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/Invoice/EInvoice/FacturaEInvoice.php @@ -174,9 +174,9 @@ class FacturaEInvoice extends AbstractService $disk = config('filesystems.default'); - if (!Storage::disk($disk)->exists($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()))) { - Storage::makeDirectory($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first())); - } + // if (!Storage::disk($disk)->exists($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()))) { + // Storage::makeDirectory($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first())); + // } $this->fac->export(Storage::disk($disk)->path($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xsig"))); @@ -386,9 +386,12 @@ class FacturaEInvoice extends AbstractService private function signDocument(): self { - // $ssl_cert = file_get_contents(base_path('e_invoice_cert.p12')); - // $this->fac->sign($ssl_cert, null, "SuperSecretPassword"); + $ssl_cert = $this->invoice->company->getInvoiceCert(); + $ssl_passphrase = $this->invoice->company->getSslPassPhrase(); + + if($ssl_cert) + $this->fac->sign($ssl_cert, null, $ssl_passphrase); return $this; } From b5ffd5f0a387cbf762bb0604d7efe3152cbf5666 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 25 May 2023 13:35:25 +1000 Subject: [PATCH 12/12] v5.5.121 --- VERSION.txt | 2 +- config/ninja.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index b48289f67fcd..4144795a019b 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.120 \ No newline at end of file +5.5.121 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index f532428b0202..15d7dd187eb0 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.120', - 'app_tag' => '5.5.120', + 'app_version' => '5.5.121', + 'app_tag' => '5.5.121', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''),