From c705784137b6e6a75e870a0fbc803beb75bd3945 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 10 Mar 2024 12:51:00 +1100 Subject: [PATCH 1/4] Handle edge case with Nordigen where account appears active, but access has expired --- app/Helpers/Bank/Nordigen/Nordigen.php | 8 ++++---- app/Jobs/Bank/ProcessBankTransactionsNordigen.php | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/Helpers/Bank/Nordigen/Nordigen.php b/app/Helpers/Bank/Nordigen/Nordigen.php index 33c62d076c8d..a07daf857bdf 100644 --- a/app/Helpers/Bank/Nordigen/Nordigen.php +++ b/app/Helpers/Bank/Nordigen/Nordigen.php @@ -97,11 +97,11 @@ class Nordigen return $it->transform($out); } catch (\Exception $e) { - if (strpos($e->getMessage(), "Invalid Account ID") !== false) { - return false; - } - throw $e; + nlog("Nordigen getAccount() failed => {$account_id} => " . $e->getMessage()); + + return false; + } } diff --git a/app/Jobs/Bank/ProcessBankTransactionsNordigen.php b/app/Jobs/Bank/ProcessBankTransactionsNordigen.php index d23e78659cec..dd7c6c657b9e 100644 --- a/app/Jobs/Bank/ProcessBankTransactionsNordigen.php +++ b/app/Jobs/Bank/ProcessBankTransactionsNordigen.php @@ -114,23 +114,26 @@ class ProcessBankTransactionsNordigen implements ShouldQueue private function updateAccount() { - if (!$this->nordigen->isAccountActive($this->bank_integration->nordigen_account_id)) { + $is_account_active = $this->nordigen->isAccountActive($this->bank_integration->nordigen_account_id); + $account = $this->nordigen->getAccount($this->bank_integration->nordigen_account_id); + + if (!$is_account_active || !$account) { $this->bank_integration->disabled_upstream = true; $this->bank_integration->save(); $this->stop_loop = false; + nlog("Nordigen: account inactive: " . $this->bank_integration->nordigen_account_id); - // @turbo124 @todo send email for expired account $this->nordigen->disabledAccountEmail($this->bank_integration); return; } - $this->nordigen_account = $this->nordigen->getAccount($this->bank_integration->nordigen_account_id); + $this->nordigen_account = $account; $this->bank_integration->disabled_upstream = false; - $this->bank_integration->bank_account_status = $this->nordigen_account['account_status']; - $this->bank_integration->balance = $this->nordigen_account['current_balance']; + $this->bank_integration->bank_account_status = $account['account_status']; + $this->bank_integration->balance = $account['current_balance']; $this->bank_integration->save(); } From 58fabe2704c64289ea371d20257a53f5fb9a5308 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 10 Mar 2024 13:01:30 +1100 Subject: [PATCH 2/4] Validation for project budgeted hours --- .../Requests/Project/StoreProjectRequest.php | 4 + .../Requests/Project/UpdateProjectRequest.php | 6 + tests/Feature/ProjectApiTest.php | 106 ++++++++++++++++++ tests/MockAccountData.php | 5 + 4 files changed, 121 insertions(+) diff --git a/app/Http/Requests/Project/StoreProjectRequest.php b/app/Http/Requests/Project/StoreProjectRequest.php index 8f8e4760df0c..3bc1bca1bc52 100644 --- a/app/Http/Requests/Project/StoreProjectRequest.php +++ b/app/Http/Requests/Project/StoreProjectRequest.php @@ -44,6 +44,7 @@ class StoreProjectRequest extends Request $rules['name'] = 'required'; $rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id; + $rules['budgeted_hours'] = 'sometimes|numeric'; if (isset($this->number)) { $rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id); @@ -74,6 +75,9 @@ class StoreProjectRequest extends Request $input['color'] = ''; } + if(array_key_exists('budgeted_hours', $input) && empty($input['budgeted_hours'])) + $input['budgeted_hours'] = 0; + $this->replace($input); } diff --git a/app/Http/Requests/Project/UpdateProjectRequest.php b/app/Http/Requests/Project/UpdateProjectRequest.php index e68c90383790..cbcf6882cf89 100644 --- a/app/Http/Requests/Project/UpdateProjectRequest.php +++ b/app/Http/Requests/Project/UpdateProjectRequest.php @@ -45,6 +45,8 @@ class UpdateProjectRequest extends Request $rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id)->ignore($this->project->id); } + $rules['budgeted_hours'] = 'sometimes|numeric'; + if ($this->file('documents') && is_array($this->file('documents'))) { $rules['documents.*'] = $this->file_validation; } elseif ($this->file('documents')) { @@ -73,6 +75,10 @@ class UpdateProjectRequest extends Request if (array_key_exists('color', $input) && is_null($input['color'])) { $input['color'] = ''; } + + if(array_key_exists('budgeted_hours', $input) && empty($input['budgeted_hours'])) { + $input['budgeted_hours'] = 0; + } $this->replace($input); } diff --git a/tests/Feature/ProjectApiTest.php b/tests/Feature/ProjectApiTest.php index f65abaae0dec..3f2b6a5ad797 100644 --- a/tests/Feature/ProjectApiTest.php +++ b/tests/Feature/ProjectApiTest.php @@ -29,6 +29,8 @@ class ProjectApiTest extends TestCase use DatabaseTransactions; use MockAccountData; + protected $faker; + protected function setUp() :void { parent::setUp(); @@ -42,6 +44,110 @@ class ProjectApiTest extends TestCase Model::reguard(); } + public function testProjectValidationForBudgetedHoursPut() + { + + $data = $this->project->toArray(); + $data['budgeted_hours'] = "aa"; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/projects/{$this->project->hashed_id}", $data); + + $response->assertStatus(422); + + } + + public function testProjectValidationForBudgetedHoursPutNull() + { + + $data = $this->project->toArray(); + $data['budgeted_hours'] = null; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/projects/{$this->project->hashed_id}", $data); + + $response->assertStatus(200); + + } + + + public function testProjectValidationForBudgetedHoursPutEmpty() + { + + $data = $this->project->toArray(); + $data['budgeted_hours'] = ""; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson("/api/v1/projects/{$this->project->hashed_id}", $data); + + $response->assertStatus(200); + + } + + + public function testProjectValidationForBudgetedHours() + { + + $data = [ + 'name' => $this->faker->firstName(), + 'client_id' => $this->client->hashed_id, + 'number' => 'duplicate', + 'budgeted_hours' => null + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/projects', $data); + + $response->assertStatus(200); + + } + + public function testProjectValidationForBudgetedHours2() + { + + $data = [ + 'name' => $this->faker->firstName(), + 'client_id' => $this->client->hashed_id, + 'number' => 'duplicate', + 'budgeted_hours' => "a" + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/projects', $data); + + $response->assertStatus(422); + + } + + public function testProjectValidationForBudgetedHours3() + { + + $data = [ + 'name' => $this->faker->firstName(), + 'client_id' => $this->client->hashed_id, + 'number' => 'duplicate', + 'budgeted_hours' => "" + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/projects', $data); + + $response->assertStatus(200); + + } + public function testProjectGetFilter() { $response = $this->withHeaders([ diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 959224548d7e..99eaf980bdb8 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -72,6 +72,11 @@ trait MockAccountData use MakesHash; use GeneratesCounter; + /** + * @var + */ + public $project; + /** * @var */ From bc7fa1d7add7d938d69293d5cceecf06bb2d8a89 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 10 Mar 2024 13:05:28 +1100 Subject: [PATCH 3/4] Add catch for null client on project --- app/Transformers/ProjectTransformer.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Transformers/ProjectTransformer.php b/app/Transformers/ProjectTransformer.php index 6f66dd23f15c..227e26571e85 100644 --- a/app/Transformers/ProjectTransformer.php +++ b/app/Transformers/ProjectTransformer.php @@ -49,6 +49,12 @@ class ProjectTransformer extends EntityTransformer public function includeClient(Project $project): \League\Fractal\Resource\Item { + + if (!$project->client) { + nlog("Project {$project->hashed_id} does not have a client attached - this project is in a bad state"); + return null; + } + $transformer = new ClientTransformer($this->serializer); return $this->includeItem($project->client, $transformer, Client::class); From fa136f592f566e5a93b84a5ea0d7247e44baaa5a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 10 Mar 2024 13:20:24 +1100 Subject: [PATCH 4/4] Updates for translations --- lang/en/texts.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lang/en/texts.php b/lang/en/texts.php index 50da2e50344d..2d7982a8d62e 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -460,7 +460,7 @@ $lang = array( 'edit_token' => 'Edit Token', 'delete_token' => 'Delete Token', 'token' => 'Token', - 'add_gateway' => 'Add Gateway', + 'add_gateway' => 'Add Payment Gateway', 'delete_gateway' => 'Delete Gateway', 'edit_gateway' => 'Edit Gateway', 'updated_gateway' => 'Successfully updated gateway', @@ -5251,6 +5251,9 @@ $lang = array( 'payment_type_help' => 'The default payment type to be used for payments', 'quote_valid_until_help' => 'The number of days that the quote is valid for', 'expense_payment_type_help' => 'The default expense payment type to be used', + 'paylater' => 'Pay in 4', + 'payment_provider' => 'Payment Provider', + ); return $lang;