diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 87f863821170..1fc12fcf867a 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -406,6 +406,79 @@ class CheckData extends Command // }); // } + private function clientPaidToDateQuery() + { + $results = \DB::select( \DB::raw(" + SELECT + clients.id as client_id, + clients.paid_to_date as client_paid_to_date, + SUM(coalesce(payments.amount - payments.refunded,0)) as payments_applied + FROM clients + INNER JOIN + payments ON + clients.id=payments.client_id + WHERE payments.status_id IN (1,4,5,6) + AND clients.is_deleted = false + AND payments.is_deleted = false + GROUP BY clients.id + HAVING payments_applied != client_paid_to_date + ORDER BY clients.id; + ") ); + + return $results; + } + + private function clientCreditPaymentables($client) + { + $results = \DB::select( \DB::raw(" + SELECT + SUM(paymentables.amount - paymentables.refunded) as credit_payment + FROM payments + LEFT JOIN paymentables + ON + payments.id = paymentables.payment_id + WHERE paymentable_type = 'App\\Models\\Credit' + AND paymentables.deleted_at is NULL + AND payments.client_id = 85; + ") ); + + return $results; + } + + private function checkPaidToDatesNew() + { + $clients_to_check = $this->clientPaidToDateQuery(); + + $this->wrong_paid_to_dates = 0; + + foreach($clients_to_check as $_client) + { + $client = Client::find($_client['client_id']); + + $credits_used_for_payments = $this->clientCreditPaymentables($client); + + $total_paid_to_date = $_client['payments_applied'] + $credits_used_for_payments['credit_payment']; + + if(round($total_paid_to_date,2) != round($_client['client_paid_to_date'],2)){ + + $this->wrong_paid_to_dates++; + + $this->logMessage($client->present()->name.' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_paid_to_date}"); + + $this->isValid = false; + + if($this->option('paid_to_date')){ + $this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->paid_to_date} to {$total_paid_to_date}"); + $client->paid_to_date = $total_paid_to_date; + $client->save(); + } + + } + + } + } + + private function checkPaidToDates() { @@ -580,6 +653,8 @@ class CheckData extends Command foreach($clients as $client) { + $client = (array)$client; + $invoice_balance = $client['invoice_balance'] - $client['credit_balance']; $ledger = CompanyLedger::where('client_id', $client['client_id'])->orderBy('id', 'DESC')->first(); diff --git a/app/Http/Controllers/TokenController.php b/app/Http/Controllers/TokenController.php index 8ef21d8bb58c..f095ca2070fe 100644 --- a/app/Http/Controllers/TokenController.php +++ b/app/Http/Controllers/TokenController.php @@ -50,6 +50,9 @@ class TokenController extends BaseController parent::__construct(); $this->token_repo = $token_repo; + + $this->middleware('password_protected')->only(['store','update']); + } /** diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index dce5168dc87f..d68dddce70df 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -13,6 +13,7 @@ namespace App\Http\Requests\Client; use App\DataMapper\ClientSettings; use App\Http\Requests\Request; +use App\Http\ValidationRules\Client\CountryCodeExistsRule; use App\Http\ValidationRules\Ninja\CanStoreClientsRule; use App\Http\ValidationRules\ValidClientGroupSettingsRule; use App\Models\Client; @@ -51,6 +52,14 @@ class StoreClientRequest extends Request $rules['number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id); } + if(isset($this->currency_code)){ + $rules['currency_code'] = 'sometimes|exists:currencies,code'; + } + + if(isset($this->country_code)){ + $rules['country_code'] = new CountryCodeExistsRule(); + } + /* Ensure we have a client name, and that all emails are unique*/ //$rules['name'] = 'required|min:1'; $rules['settings'] = new ValidClientGroupSettingsRule(); @@ -133,6 +142,7 @@ class StoreClientRequest extends Request // 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']), //'required' => trans('validation.required', ['attribute' => 'email']), 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), + 'currency_code' => 'Currency code does not exist', ]; } @@ -158,6 +168,9 @@ class StoreClientRequest extends Request return $item->code == $code; })->first(); - return (string) $currency->id; + if($currency) + return (string) $currency->id; + + return ""; } } diff --git a/app/Http/ValidationRules/Client/CountryCodeExistsRule.php b/app/Http/ValidationRules/Client/CountryCodeExistsRule.php new file mode 100644 index 000000000000..f8fd2089712b --- /dev/null +++ b/app/Http/ValidationRules/Client/CountryCodeExistsRule.php @@ -0,0 +1,60 @@ +checkIfCodeExists($value); //if it exists, return false! + } + + /** + * @return string + */ + public function message() + { + return 'Country code does not exist'; + + } + + /** + * @return bool + */ + private function checkIfCodeExists($value) : bool + { + $country = Country::where('iso_3166_2', $value) + ->orWhere('iso_3166_3', $value) + ->exists(); + + if ($country) + return true; + + return false; + } +} diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index 077ac8ebd856..0001deac7888 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -1032,10 +1032,12 @@ class CompanyImport implements ShouldQueue unset($user_array['hashed_id']); unset($user_array['id']); - $new_user = User::firstOrNew( + /*Make sure we are searching for archived users also and restore if we find them.*/ + + $new_user = User::withTrashed()->firstOrNew( ['email' => $user->email], $user_array, - ); + )->restore(); $new_user->account_id = $this->account->id; $new_user->save(['timestamps' => false]); @@ -1062,10 +1064,10 @@ class CompanyImport implements ShouldQueue unset($cu_array['company_id']); unset($cu_array['user_id']); - $new_cu = CompanyUser::firstOrNew( + $new_cu = CompanyUser::withTrashed()->firstOrNew( ['user_id' => $user_id, 'company_id' => $this->company->id], $cu_array, - ); + )->restore(); $new_cu->account_id = $this->account->id; $new_cu->save(['timestamps' => false]); diff --git a/app/Models/Client.php b/app/Models/Client.php index 520bd5f59857..55ac5fe02471 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -530,7 +530,7 @@ class Client extends BaseModel implements HasLocalePreference } } - if ($this->country->iso_3166_3 == 'GBR' && in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) { + if ($this->country && $this->country->iso_3166_3 == 'GBR' && in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) { foreach ($pms as $pm) { if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) { $cg = CompanyGateway::find($pm['company_gateway_id']); diff --git a/tests/Feature/ClientApiTest.php b/tests/Feature/ClientApiTest.php index affd7461d3c6..a481c3775f79 100644 --- a/tests/Feature/ClientApiTest.php +++ b/tests/Feature/ClientApiTest.php @@ -10,10 +10,12 @@ */ namespace Tests\Feature; +use App\Models\Country; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; +use Illuminate\Validation\ValidationException; use Tests\MockAccountData; use Tests\TestCase; @@ -40,6 +42,77 @@ class ClientApiTest extends TestCase Model::reguard(); } + public function testClientCountryCodeValidationTrue() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'country_code' => 'AM' + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(200); + + } + + + public function testClientCountryCodeValidationTrueIso3() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'country_code' => 'ARM' + ]; + + $response = false; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(200); + + } + + + + public function testClientCountryCodeValidationFalse() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'country_code' => 'AdfdfdfM' + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + + $response->assertStatus(302); + + } + public function testClientPost() { $data = [ @@ -174,4 +247,45 @@ class ClientApiTest extends TestCase $this->assertTrue($arr['data'][0]['is_deleted']); } + + public function testClientCurrencyCodeValidationTrue() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'currency_code' => 'USD' + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + + $response->assertStatus(200); + + } + + public function testClientCurrencyCodeValidationFalse() + { + + $data = [ + 'name' => $this->faker->firstName, + 'id_number' => 'Coolio', + 'currency_code' => 'R' + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + + $response->assertStatus(302); + + } + + + }