From 539fc1dcfdf4d84617ccc11731a59978903cdf21 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 11 Jan 2022 15:08:39 +1100 Subject: [PATCH 01/12] Change location for test png --- tests/Integration/CompanyLedgerTest.php | 2 +- tests/MockAccountData.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/CompanyLedgerTest.php b/tests/Integration/CompanyLedgerTest.php index 76eb3eb78f..c5ece3e949 100644 --- a/tests/Integration/CompanyLedgerTest.php +++ b/tests/Integration/CompanyLedgerTest.php @@ -84,7 +84,7 @@ class CompanyLedgerTest extends TestCase $settings = CompanySettings::defaults(); - $settings->company_logo = 'https://app.invoiceninja.com/favicon-v2.png'; + $settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png'; $settings->website = 'www.invoiceninja.com'; $settings->address1 = 'Address 1'; $settings->address2 = 'Address 2'; diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 7983a9d9d4..b2a6276ad5 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -192,7 +192,7 @@ trait MockAccountData $settings = CompanySettings::defaults(); - $settings->company_logo = 'https://app.invoiceninja.com/favicon-v2.png'; + $settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png'; // $settings->company_logo = asset('images/new_logo.png'); $settings->website = 'www.invoiceninja.com'; $settings->address1 = 'Address 1'; From f66c3076f18a7a0566ab6795d833fe6b99f2edd9 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 11 Jan 2022 19:08:08 +1100 Subject: [PATCH 02/12] fixes for adding users into the production system --- app/Http/Requests/User/StoreUserRequest.php | 2 +- .../ValidationRules/Ninja/CanAddUserRule.php | 19 ++++++++++++------- app/Models/Company.php | 2 +- app/Repositories/UserRepository.php | 8 ++++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/app/Http/Requests/User/StoreUserRequest.php b/app/Http/Requests/User/StoreUserRequest.php index f9275bd525..4a60a339e0 100644 --- a/app/Http/Requests/User/StoreUserRequest.php +++ b/app/Http/Requests/User/StoreUserRequest.php @@ -58,7 +58,7 @@ class StoreUserRequest extends Request { $input = $this->all(); -//unique user rule - check company_user table for user_id / company_id / account_id if none exist we can add the user. ELSE return false + //unique user rule - check company_user table for user_id / company_id / account_id if none exist we can add the user. ELSE return false if(array_key_exists('email', $input)) $input['email'] = trim($input['email']); diff --git a/app/Http/ValidationRules/Ninja/CanAddUserRule.php b/app/Http/ValidationRules/Ninja/CanAddUserRule.php index 6333edf1ab..1a26d1035b 100644 --- a/app/Http/ValidationRules/Ninja/CanAddUserRule.php +++ b/app/Http/ValidationRules/Ninja/CanAddUserRule.php @@ -33,13 +33,18 @@ class CanAddUserRule implements Rule public function passes($attribute, $value) { - $count = CompanyUser::query() - ->where('company_user.account_id', auth()->user()->account_id) - ->join('users', 'users.id', '=', 'company_user.user_id') - ->whereNull('users.deleted_at') - ->whereNull('company_user.deleted_at') - ->distinct() - ->count('company_user.user_id'); + /* If the user is active then we can add them to the company */ + if(User::where('email', request()->input('email'))->where('account_id', auth()->user()->account_id)->where('is_deleted',0)->exists()) + return true; + + /* Check that we have sufficient quota to allow this to happen */ + $count = CompanyUser::query() + ->where('company_user.account_id', auth()->user()->account_id) + ->join('users', 'users.id', '=', 'company_user.user_id') + ->whereNull('users.deleted_at') + ->whereNull('company_user.deleted_at') + ->distinct() + ->count('company_user.user_id'); return $count < auth()->user()->company()->account->num_users; diff --git a/app/Models/Company.php b/app/Models/Company.php index 86554fe500..ec7497f1eb 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -171,7 +171,7 @@ class Company extends BaseModel public function users() { - return $this->hasManyThrough(User::class, CompanyUser::class, 'company_id', 'id', 'id', 'user_id'); + return $this->hasManyThrough(User::class, CompanyUser::class, 'company_id', 'id', 'id', 'user_id')->withTrashed(); } public function expense_categories() diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 6e91d344cc..fdad645cde 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -189,6 +189,14 @@ class UserRepository extends BaseRepository return; } + if (Ninja::isHosted()) { + + $count = User::where('account_id', auth()->user()->account_id)->count(); + if($count >= auth()->user()->account->num_users) + return; + + } + $user->is_deleted = false; $user->save(); $user->restore(); From 058c9ab04913340e55acd4113ce346498140f139 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 11 Jan 2022 19:17:52 +1100 Subject: [PATCH 03/12] Only send payment receipt to a single contact --- app/Services/Payment/SendEmail.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Services/Payment/SendEmail.php b/app/Services/Payment/SendEmail.php index 978a038df2..3544a53c7e 100644 --- a/app/Services/Payment/SendEmail.php +++ b/app/Services/Payment/SendEmail.php @@ -37,6 +37,8 @@ class SendEmail $this->payment->client->contacts->each(function ($contact) { if ($contact->email) { EmailPayment::dispatchNow($this->payment, $this->payment->company, $contact); + return false; + //11-01-2021 only send payment receipt to the first contact } }); } From 3e36f335b4ded95f41e6b6157a1633ddd7fac1de Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 11 Jan 2022 19:42:00 +1100 Subject: [PATCH 04/12] Fixes for required fields --- app/Http/Livewire/RequiredClientInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Livewire/RequiredClientInfo.php b/app/Http/Livewire/RequiredClientInfo.php index 3431cbd0ed..fc500ea877 100644 --- a/app/Http/Livewire/RequiredClientInfo.php +++ b/app/Http/Livewire/RequiredClientInfo.php @@ -150,7 +150,7 @@ class RequiredClientInfo extends Component } if (Str::startsWith($field['name'], 'contact_')) { - if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field})) { + if (empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) { // if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} == 840) { $this->show_form = true; } else { From 8e3b90494bcb6bab93862985d21f757546e848ff Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 11 Jan 2022 20:01:03 +1100 Subject: [PATCH 05/12] remove logging --- app/Services/Client/Statement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/Client/Statement.php b/app/Services/Client/Statement.php index 549fec257d..886579d8a3 100644 --- a/app/Services/Client/Statement.php +++ b/app/Services/Client/Statement.php @@ -236,7 +236,7 @@ class Statement private function invoiceStatuses() :array { $status = 'all'; -nlog($this->options); + if(array_key_exists('status', $this->options)) $status = $this->options['status']; From f3947b104a063adb2d69845f1d1d92fe7195a049 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 12 Jan 2022 13:05:07 +1100 Subject: [PATCH 06/12] change deletePdf() to touchPdf() --- app/Http/Controllers/InvoiceController.php | 2 +- app/Services/Invoice/MarkPaid.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index e2c9131521..f23b049666 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -401,7 +401,7 @@ class InvoiceController extends BaseController $invoice = $this->invoice_repo->save($request->all(), $invoice); - $invoice->service()->triggeredActions($request)->deletePdf()->touchPdf(); + $invoice->service()->triggeredActions($request)->touchPdf(); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); diff --git a/app/Services/Invoice/MarkPaid.php b/app/Services/Invoice/MarkPaid.php index 49443d4168..90d51588c4 100644 --- a/app/Services/Invoice/MarkPaid.php +++ b/app/Services/Invoice/MarkPaid.php @@ -88,7 +88,7 @@ class MarkPaid extends AbstractService $this->invoice ->service() ->applyNumber() - ->deletePdf() + // ->deletePdf() ->touchPdf() ->save(); From 5f51ea9002fbc3df6fbccb66ef84357295c897e2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 12 Jan 2022 14:40:05 +1100 Subject: [PATCH 07/12] Fixes for payment request --- .../Requests/Payment/StorePaymentRequest.php | 9 +- .../Payment/ValidInvoicesRules.php | 5 + .../Payment/ValidRefundableRequest.php | 5 + .../PaymentAmountsBalanceRule.php | 8 +- .../Payments/StorePaymentValidationTest.php | 167 ++++++++++++++++++ 5 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 tests/Feature/Payments/StorePaymentValidationTest.php diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php index 2deffed782..c74f91d78a 100644 --- a/app/Http/Requests/Payment/StorePaymentRequest.php +++ b/app/Http/Requests/Payment/StorePaymentRequest.php @@ -54,7 +54,10 @@ class StorePaymentRequest extends Request if (isset($input['invoices']) && is_array($input['invoices']) !== false) { foreach ($input['invoices'] as $key => $value) { $input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']); - $invoices_total += $value['amount']; + + if(array_key_exists('amount', $value)) + $invoices_total += $value['amount']; + } } @@ -91,12 +94,12 @@ class StorePaymentRequest extends Request public function rules() { $rules = [ - 'amount' => 'numeric|required', + 'amount' => 'sometimes|numeric', 'amount' => [new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule()], 'client_id' => 'bail|required|exists:clients,id', 'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id', + 'invoices.*.amount' => 'bail|required', 'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()), - 'invoices.*.amount' => 'required', 'credits.*.credit_id' => 'bail|required|exists:credits,id', 'credits.*.credit_id' => new ValidCreditsRules($this->all()), 'credits.*.amount' => ['required', new CreditsSumRule($this->all())], diff --git a/app/Http/ValidationRules/Payment/ValidInvoicesRules.php b/app/Http/ValidationRules/Payment/ValidInvoicesRules.php index 0bc63580eb..f3a333a091 100644 --- a/app/Http/ValidationRules/Payment/ValidInvoicesRules.php +++ b/app/Http/ValidationRules/Payment/ValidInvoicesRules.php @@ -57,6 +57,11 @@ class ValidInvoicesRules implements Rule $unique_array[] = $invoice['invoice_id']; + if(!array_key_exists('amount', $invoice)){ + $this->error_msg = ctrans('texts.amount') . " required"; + return false; + } + $inv = Invoice::whereId($invoice['invoice_id'])->first(); if (! $inv) { diff --git a/app/Http/ValidationRules/Payment/ValidRefundableRequest.php b/app/Http/ValidationRules/Payment/ValidRefundableRequest.php index b29c6ab06a..7abba2b787 100644 --- a/app/Http/ValidationRules/Payment/ValidRefundableRequest.php +++ b/app/Http/ValidationRules/Payment/ValidRefundableRequest.php @@ -79,6 +79,11 @@ class ValidRefundableRequest implements Rule { $invoice = Invoice::whereId($invoice['invoice_id'])->whereCompanyId($payment->company_id)->withTrashed()->first(); + if(!$invoice){ + $this->error_msg = "Invoice not found for refund"; + return false; + } + if ($payment->invoices()->exists()) { $paymentable_invoice = $payment->invoices->where('id', $invoice->id)->first(); diff --git a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php index 2097b1b77c..be8f0d60ec 100644 --- a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php +++ b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php @@ -59,13 +59,17 @@ class PaymentAmountsBalanceRule implements Rule if (request()->input('credits') && is_array(request()->input('credits'))) { foreach (request()->input('credits') as $credit) { - $payment_amounts += $credit['amount']; + + if(array_key_exists('amount', $credit)) + $payment_amounts += $credit['amount']; } } if (request()->input('invoices') && is_array(request()->input('invoices'))) { foreach (request()->input('invoices') as $invoice) { - $invoice_amounts += $invoice['amount']; + + if(array_key_exists('amount', $invoice)) + $invoice_amounts += $invoice['amount']; } } else { return true; diff --git a/tests/Feature/Payments/StorePaymentValidationTest.php b/tests/Feature/Payments/StorePaymentValidationTest.php new file mode 100644 index 0000000000..7a4d11a44d --- /dev/null +++ b/tests/Feature/Payments/StorePaymentValidationTest.php @@ -0,0 +1,167 @@ +faker = \Faker\Factory::create(); + + Model::reguard(); + + $this->makeTestData(); + + + $this->withoutMiddleware( + ThrottleRequests::class + ); + } + + + public function testValidPayment() + { + + $data = [ + 'amount' => 0, + 'client_id' => $this->client->hashed_id, + 'invoices' => [ + ], + 'date' => '2019/12/12', + ]; + + $response = false; + + try { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/payments/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($e->validator->getMessageBag()); + } + + $response->assertStatus(200); + + } + + + public function testValidPaymentWithAmount() + { + + $data = [ + 'amount' => 0, + 'client_id' => $this->client->hashed_id, + 'invoices' => [ + [ + 'invoice_id' => $this->invoice->hashed_id, + 'amount' => 10, + ], + ], + 'credits' => [ + [ + 'credit_id' => $this->credit->hashed_id, + 'amount' => 5 + ] + ], + 'date' => '2019/12/12', + ]; + + $response = false; + + try { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/payments/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($e->validator->getMessageBag()); + } + + $response->assertStatus(200); + + } + + public function testValidPaymentWithInvalidData() + { + + + $data = [ + 'amount' => 0, + 'client_id' => $this->client->hashed_id, + 'invoices' => [ + [ + 'invoice_id' => $this->invoice->hashed_id, + ], + ], + 'credits' => [ + [ + 'credit_id' => $this->credit->hashed_id, + 'amount' => 5 + ] + ], + 'date' => '2019/12/12', + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/payments/', $data); + }catch(ValidationException $e){ + $response->assertStatus(302); + } + + } + + + +} + From 99db919d609505faebf0fe285fb3650a272e27fa Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 12 Jan 2022 17:29:28 +1100 Subject: [PATCH 08/12] Logging for balances --- app/Http/ValidationRules/PaymentAmountsBalanceRule.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php index 2097b1b77c..6fa9d089f9 100644 --- a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php +++ b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php @@ -71,7 +71,8 @@ class PaymentAmountsBalanceRule implements Rule return true; } - - return $payment_amounts >= $invoice_amounts; + nlog($payment_amounts ." >= " . $invoice_amounts); + + return $payment_amounts >= $invoice_amounts; } } From 5bd0b043e0b6682f7acf0c2c76ec3b1e313680af Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 12 Jan 2022 17:34:47 +1100 Subject: [PATCH 09/12] Minor fixes for playful design --- app/Http/ValidationRules/PaymentAmountsBalanceRule.php | 3 +++ resources/views/pdf-designs/playful.html | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php index be8f0d60ec..6fe72aa31e 100644 --- a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php +++ b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php @@ -75,6 +75,9 @@ class PaymentAmountsBalanceRule implements Rule return true; } +nlog(request()->input('invoices')); +nlog($payment_amounts); +nlog($invoice_amounts); return $payment_amounts >= $invoice_amounts; } diff --git a/resources/views/pdf-designs/playful.html b/resources/views/pdf-designs/playful.html index 550b202b2f..a9c6135e5e 100644 --- a/resources/views/pdf-designs/playful.html +++ b/resources/views/pdf-designs/playful.html @@ -38,7 +38,10 @@ border-radius: 10px; } - #entity-details p { margin-right: 20px; } + #entity-details p { + margin-right: 20px; + white-space: nowrap; + } .header-wrapper #entity-details { width: 100%; From 848cb6ae4caaee14c05f111ae08bee9af3447e45 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 12 Jan 2022 17:55:12 +1100 Subject: [PATCH 10/12] Do not translate date if none is present --- app/Utils/Traits/MakesDates.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Utils/Traits/MakesDates.php b/app/Utils/Traits/MakesDates.php index 02cac3cdab..1ca67e1e0c 100644 --- a/app/Utils/Traits/MakesDates.php +++ b/app/Utils/Traits/MakesDates.php @@ -99,6 +99,9 @@ trait MakesDates public function translateDate($date, $format, $locale) { + if(empty($date)) + return ''; + Carbon::setLocale($locale); try { From c26387a3767196051e69be67fde9d9e6b56e835f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 12 Jan 2022 20:29:10 +1100 Subject: [PATCH 11/12] Fixes for import --- app/Http/Controllers/StaticController.php | 37 +++++++++++++++++++++++ app/Jobs/Util/Import.php | 13 ++++++-- app/Services/Invoice/AutoBillInvoice.php | 2 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/StaticController.php b/app/Http/Controllers/StaticController.php index 1cb4ccf1ff..20e99db5d6 100644 --- a/app/Http/Controllers/StaticController.php +++ b/app/Http/Controllers/StaticController.php @@ -23,6 +23,43 @@ use Illuminate\Http\Response; class StaticController extends BaseController { + /** + * Show the list of Invoices. + * + * @param InvoiceFilters $filters The filters + * + * @return Response + * + * @OA\Get( + * path="/api/v1/statics", + * operationId="getStatics", + * tags={"statics"}, + * summary="Gets a list of statics", + * description="Lists all statics", + * + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="A list of static data", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + */ public function __invoke() { diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php index 773ef83262..627619656c 100644 --- a/app/Jobs/Util/Import.php +++ b/app/Jobs/Util/Import.php @@ -1866,10 +1866,19 @@ class Import implements ShouldQueue private function processNinjaTokens(array $data) { + nlog("attempting to process Ninja Tokens"); - if(Ninja::isHosted()) - \Modules\Admin\Jobs\Account\NinjaUser::dispatchNow($data, $this->company); + if(Ninja::isHosted()){ + + try{ + \Modules\Admin\Jobs\Account\NinjaUser::dispatchNow($data, $this->company); + } + catch(\Exception $e){ + nlog($e->getMessage()); + } + + } } diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 025e9dcec9..17a5119091 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -125,7 +125,7 @@ class AutoBillInvoice extends AbstractService } catch(\Exception $e){ nlog("payment NOT captured for ". $this->invoice->number . " with error " . $e->getMessage()); - // nlog($e->getMessage()); + $this->invoice->service()->removeUnpaidGatewayFees()->save(); } if($payment){ From 27655905fcc5e070c735a02bc2cbbe71a77c2cab Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 12 Jan 2022 21:52:50 +1100 Subject: [PATCH 12/12] v5.3.45 --- VERSION.txt | 2 +- config/ninja.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 1690fa814b..96fb010e76 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.3.44 \ No newline at end of file +5.3.45 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index a485bc0f57..155797ed41 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -14,8 +14,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.3.44', - 'app_tag' => '5.3.44', + 'app_version' => '5.3.45', + 'app_tag' => '5.3.45', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''),