mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-08 05:34:30 -04:00
Merge pull request #6928 from turbo124/v5-develop
Custom validation rules for client countries
This commit is contained in:
commit
42e40461db
@ -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()
|
private function checkPaidToDates()
|
||||||
{
|
{
|
||||||
@ -580,6 +653,8 @@ class CheckData extends Command
|
|||||||
|
|
||||||
foreach($clients as $client)
|
foreach($clients as $client)
|
||||||
{
|
{
|
||||||
|
$client = (array)$client;
|
||||||
|
|
||||||
$invoice_balance = $client['invoice_balance'] - $client['credit_balance'];
|
$invoice_balance = $client['invoice_balance'] - $client['credit_balance'];
|
||||||
|
|
||||||
$ledger = CompanyLedger::where('client_id', $client['client_id'])->orderBy('id', 'DESC')->first();
|
$ledger = CompanyLedger::where('client_id', $client['client_id'])->orderBy('id', 'DESC')->first();
|
||||||
|
@ -50,6 +50,9 @@ class TokenController extends BaseController
|
|||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->token_repo = $token_repo;
|
$this->token_repo = $token_repo;
|
||||||
|
|
||||||
|
$this->middleware('password_protected')->only(['store','update']);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Http\Requests\Client;
|
|||||||
|
|
||||||
use App\DataMapper\ClientSettings;
|
use App\DataMapper\ClientSettings;
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
|
use App\Http\ValidationRules\Client\CountryCodeExistsRule;
|
||||||
use App\Http\ValidationRules\Ninja\CanStoreClientsRule;
|
use App\Http\ValidationRules\Ninja\CanStoreClientsRule;
|
||||||
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
|
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
@ -51,6 +52,14 @@ class StoreClientRequest extends Request
|
|||||||
$rules['number'] = Rule::unique('clients')->where('company_id', auth()->user()->company()->id);
|
$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*/
|
/* Ensure we have a client name, and that all emails are unique*/
|
||||||
//$rules['name'] = 'required|min:1';
|
//$rules['name'] = 'required|min:1';
|
||||||
$rules['settings'] = new ValidClientGroupSettingsRule();
|
$rules['settings'] = new ValidClientGroupSettingsRule();
|
||||||
@ -133,6 +142,7 @@ class StoreClientRequest extends Request
|
|||||||
// 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']),
|
// 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']),
|
||||||
//'required' => trans('validation.required', ['attribute' => 'email']),
|
//'required' => trans('validation.required', ['attribute' => 'email']),
|
||||||
'contacts.*.email.required' => ctrans('validation.email', ['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;
|
return $item->code == $code;
|
||||||
})->first();
|
})->first();
|
||||||
|
|
||||||
|
if($currency)
|
||||||
return (string) $currency->id;
|
return (string) $currency->id;
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
app/Http/ValidationRules/Client/CountryCodeExistsRule.php
Normal file
60
app/Http/ValidationRules/Client/CountryCodeExistsRule.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Credit Ninja (https://creditninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/creditninja/creditninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Credit Ninja LLC (https://creditninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\ValidationRules\Client;
|
||||||
|
|
||||||
|
use App\Models\Country;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UniqueCreditNumberRule.
|
||||||
|
*/
|
||||||
|
class CountryCodeExistsRule implements Rule
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
return $this->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;
|
||||||
|
}
|
||||||
|
}
|
@ -1032,10 +1032,12 @@ class CompanyImport implements ShouldQueue
|
|||||||
unset($user_array['hashed_id']);
|
unset($user_array['hashed_id']);
|
||||||
unset($user_array['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],
|
['email' => $user->email],
|
||||||
$user_array,
|
$user_array,
|
||||||
);
|
)->restore();
|
||||||
|
|
||||||
$new_user->account_id = $this->account->id;
|
$new_user->account_id = $this->account->id;
|
||||||
$new_user->save(['timestamps' => false]);
|
$new_user->save(['timestamps' => false]);
|
||||||
@ -1062,10 +1064,10 @@ class CompanyImport implements ShouldQueue
|
|||||||
unset($cu_array['company_id']);
|
unset($cu_array['company_id']);
|
||||||
unset($cu_array['user_id']);
|
unset($cu_array['user_id']);
|
||||||
|
|
||||||
$new_cu = CompanyUser::firstOrNew(
|
$new_cu = CompanyUser::withTrashed()->firstOrNew(
|
||||||
['user_id' => $user_id, 'company_id' => $this->company->id],
|
['user_id' => $user_id, 'company_id' => $this->company->id],
|
||||||
$cu_array,
|
$cu_array,
|
||||||
);
|
)->restore();
|
||||||
|
|
||||||
$new_cu->account_id = $this->account->id;
|
$new_cu->account_id = $this->account->id;
|
||||||
$new_cu->save(['timestamps' => false]);
|
$new_cu->save(['timestamps' => false]);
|
||||||
|
@ -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) {
|
foreach ($pms as $pm) {
|
||||||
if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) {
|
if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) {
|
||||||
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
||||||
|
@ -10,10 +10,12 @@
|
|||||||
*/
|
*/
|
||||||
namespace Tests\Feature;
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Country;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
use Tests\MockAccountData;
|
use Tests\MockAccountData;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@ -40,6 +42,77 @@ class ClientApiTest extends TestCase
|
|||||||
Model::reguard();
|
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()
|
public function testClientPost()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
@ -174,4 +247,45 @@ class ClientApiTest extends TestCase
|
|||||||
|
|
||||||
$this->assertTrue($arr['data'][0]['is_deleted']);
|
$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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user