mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-12-12 01:42:32 +00:00
Code cleanup.
This commit is contained in:
@@ -29,6 +29,4 @@ namespace FireflyIII\Validation\Account;
|
||||
*
|
||||
* Trait AccountValidatorProperties
|
||||
*/
|
||||
trait AccountValidatorProperties
|
||||
{
|
||||
}
|
||||
trait AccountValidatorProperties {}
|
||||
|
||||
@@ -31,11 +31,6 @@ use FireflyIII\Models\AccountType;
|
||||
*/
|
||||
trait DepositValidation
|
||||
{
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateDepositDestination(array $array): bool
|
||||
{
|
||||
$result = null;
|
||||
@@ -79,26 +74,10 @@ trait DepositValidation
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $accountTypes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function canCreateTypes(array $accountTypes): bool;
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
abstract protected function findExistingAccount(array $validTypes, array $data): ?Account;
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateDepositSource(array $array): bool
|
||||
{
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
@@ -113,11 +92,11 @@ trait DepositValidation
|
||||
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
if (null === $accountId &&
|
||||
null === $accountName &&
|
||||
null === $accountIban &&
|
||||
null === $accountNumber &&
|
||||
false === $this->canCreateTypes($validTypes)) {
|
||||
if (null === $accountId
|
||||
&& null === $accountName
|
||||
&& null === $accountIban
|
||||
&& null === $accountNumber
|
||||
&& false === $this->canCreateTypes($validTypes)) {
|
||||
// if both values are NULL return false,
|
||||
// because the source of a deposit can't be created.
|
||||
// (this never happens).
|
||||
@@ -131,6 +110,7 @@ trait DepositValidation
|
||||
$existing = $this->findExistingAccount($validTypes, ['iban' => $accountIban], true);
|
||||
if (null !== $existing) {
|
||||
$this->sourceError = (string)trans('validation.deposit_src_iban_exists');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,6 @@ use FireflyIII\Models\AccountType;
|
||||
*/
|
||||
trait LiabilityValidation
|
||||
{
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateLCDestination(array $array): bool
|
||||
{
|
||||
app('log')->debug('Now in validateLCDestination', $array);
|
||||
@@ -50,30 +45,31 @@ trait LiabilityValidation
|
||||
if (null !== $accountId) {
|
||||
if (AccountType::LIABILITY_CREDIT !== $this->source?->accountType?->type) {
|
||||
app('log')->error('Source account is not a liability.');
|
||||
|
||||
return false;
|
||||
}
|
||||
$result = $this->findExistingAccount($validTypes, $array);
|
||||
if (null === $result) {
|
||||
app('log')->error('Destination account is not a liability.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (null !== $accountName && '' !== $accountName) {
|
||||
app('log')->debug('Destination ID is null, now we can assume the destination is a (new) liability credit account.');
|
||||
|
||||
return true;
|
||||
}
|
||||
app('log')->error('Destination ID is null, but destination name is also NULL.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source of a liability credit must be a liability or liability credit account.
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateLCSource(array $array): bool
|
||||
{
|
||||
@@ -87,10 +83,12 @@ trait LiabilityValidation
|
||||
$result = $this->findExistingAccount(config('firefly.valid_liabilities'), $array);
|
||||
if (null === $result) {
|
||||
app('log')->error('Did not find a liability account, return false.');
|
||||
|
||||
return false;
|
||||
}
|
||||
app('log')->debug(sprintf('Return true, found #%d ("%s")', $result->id, $result->name));
|
||||
$this->setSource($result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,11 +32,6 @@ use FireflyIII\Models\AccountType;
|
||||
*/
|
||||
trait OBValidation
|
||||
{
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateOBDestination(array $array): bool
|
||||
{
|
||||
$result = null;
|
||||
@@ -78,20 +73,11 @@ trait OBValidation
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $accountTypes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function canCreateTypes(array $accountTypes): bool;
|
||||
|
||||
/**
|
||||
* Source of an opening balance can either be an asset account
|
||||
* or an "initial balance account". The latter can be created.
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateOBSource(array $array): bool
|
||||
{
|
||||
@@ -138,6 +124,7 @@ trait OBValidation
|
||||
|
||||
// set the source to be a (dummy) initial balance account.
|
||||
$account = new Account();
|
||||
|
||||
/** @var AccountType $accountType */
|
||||
$accountType = AccountType::whereType(AccountType::INITIAL_BALANCE)->first();
|
||||
$account->accountType = $accountType;
|
||||
|
||||
@@ -34,11 +34,6 @@ trait ReconciliationValidation
|
||||
public ?Account $destination;
|
||||
public ?Account $source;
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateReconciliationDestination(array $array): bool
|
||||
{
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
@@ -71,10 +66,6 @@ trait ReconciliationValidation
|
||||
|
||||
/**
|
||||
* Basically the same check
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateReconciliationSource(array $array): bool
|
||||
{
|
||||
@@ -86,6 +77,7 @@ trait ReconciliationValidation
|
||||
if (null === $accountId && null === $accountName) {
|
||||
app('log')->debug('The source is valid because ID and name are NULL.');
|
||||
$this->setSource(new Account());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,6 @@ use FireflyIII\Models\Account;
|
||||
*/
|
||||
trait TransferValidation
|
||||
{
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateTransferDestination(array $array): bool
|
||||
{
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
@@ -72,26 +67,10 @@ trait TransferValidation
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $accountTypes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function canCreateTypes(array $accountTypes): bool;
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
abstract protected function findExistingAccount(array $validTypes, array $data): ?Account;
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateTransferSource(array $array): bool
|
||||
{
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
|
||||
@@ -31,11 +31,6 @@ use FireflyIII\Models\AccountType;
|
||||
*/
|
||||
trait WithdrawalValidation
|
||||
{
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateGenericSource(array $array): bool
|
||||
{
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
@@ -67,26 +62,10 @@ trait WithdrawalValidation
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $accountTypes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function canCreateTypes(array $accountTypes): bool;
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
abstract protected function findExistingAccount(array $validTypes, array $data): ?Account;
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateWithdrawalDestination(array $array): bool
|
||||
{
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
@@ -112,6 +91,7 @@ trait WithdrawalValidation
|
||||
$type = $found->accountType->type;
|
||||
if (in_array($type, $validTypes, true)) {
|
||||
$this->setDestination($found);
|
||||
|
||||
return true;
|
||||
}
|
||||
// todo explain error in log message.
|
||||
@@ -128,6 +108,7 @@ trait WithdrawalValidation
|
||||
$existing = $this->findExistingAccount($validTypes, ['iban' => $accountIban], true);
|
||||
if (null !== $existing) {
|
||||
$this->destError = (string)trans('validation.withdrawal_dest_iban_exists');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -136,11 +117,6 @@ trait WithdrawalValidation
|
||||
return true === $this->canCreateTypes($validTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateWithdrawalSource(array $array): bool
|
||||
{
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
|
||||
@@ -75,17 +75,11 @@ class AccountValidator
|
||||
$this->userGroupAccountRepository = app(UserGroupAccountRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
public function getSource(): ?Account
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account|null $account
|
||||
*/
|
||||
public function setSource(?Account $account): void
|
||||
{
|
||||
if (null === $account) {
|
||||
@@ -97,9 +91,6 @@ class AccountValidator
|
||||
$this->source = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account|null $account
|
||||
*/
|
||||
public function setDestination(?Account $account): void
|
||||
{
|
||||
if (null === $account) {
|
||||
@@ -111,40 +102,24 @@ class AccountValidator
|
||||
$this->destination = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $transactionType
|
||||
*/
|
||||
public function setTransactionType(string $transactionType): void
|
||||
{
|
||||
app('log')->debug(sprintf('Transaction type for validator is now "%s".', ucfirst($transactionType)));
|
||||
$this->transactionType = ucfirst($transactionType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->accountRepository->setUser($user);
|
||||
$this->useUserGroupRepository = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserGroup $userGroup
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUserGroup(UserGroup $userGroup): void
|
||||
{
|
||||
$this->userGroupAccountRepository->setUserGroup($userGroup);
|
||||
$this->useUserGroupRepository = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateDestination(array $array): bool
|
||||
{
|
||||
app('log')->debug('Now in AccountValidator::validateDestination()', $array);
|
||||
@@ -154,83 +129,100 @@ class AccountValidator
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($this->transactionType) {
|
||||
default:
|
||||
$this->destError = sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType);
|
||||
app('log')->error(sprintf('AccountValidator::validateDestination cannot handle "%s", so it will always return false.', $this->transactionType));
|
||||
|
||||
$result = false;
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::WITHDRAWAL:
|
||||
$result = $this->validateWithdrawalDestination($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::DEPOSIT:
|
||||
$result = $this->validateDepositDestination($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::TRANSFER:
|
||||
$result = $this->validateTransferDestination($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::OPENING_BALANCE:
|
||||
$result = $this->validateOBDestination($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::LIABILITY_CREDIT:
|
||||
$result = $this->validateLCDestination($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::RECONCILIATION:
|
||||
$result = $this->validateReconciliationDestination($array);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateSource(array $array): bool
|
||||
{
|
||||
app('log')->debug('Now in AccountValidator::validateSource()', $array);
|
||||
|
||||
switch ($this->transactionType) {
|
||||
default:
|
||||
app('log')->error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType));
|
||||
$result = $this->validateGenericSource($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::WITHDRAWAL:
|
||||
$result = $this->validateWithdrawalSource($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::DEPOSIT:
|
||||
$result = $this->validateDepositSource($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::TRANSFER:
|
||||
$result = $this->validateTransferSource($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::OPENING_BALANCE:
|
||||
$result = $this->validateOBSource($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::LIABILITY_CREDIT:
|
||||
$result = $this->validateLCSource($array);
|
||||
|
||||
break;
|
||||
|
||||
case TransactionType::RECONCILIATION:
|
||||
app('log')->debug('Calling validateReconciliationSource');
|
||||
$result = $this->validateReconciliationSource($array);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $accountTypes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function canCreateTypes(array $accountTypes): bool
|
||||
{
|
||||
app('log')->debug('Can we create any of these types?', $accountTypes);
|
||||
|
||||
/** @var string $accountType */
|
||||
foreach ($accountTypes as $accountType) {
|
||||
if ($this->canCreateType($accountType)) {
|
||||
@@ -244,11 +236,6 @@ class AccountValidator
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accountType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function canCreateType(string $accountType): bool
|
||||
{
|
||||
$canCreate = [AccountType::EXPENSE, AccountType::REVENUE, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT];
|
||||
@@ -260,11 +247,6 @@ class AccountValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param array $data
|
||||
* @param bool $inverse
|
||||
*
|
||||
* @return Account|null
|
||||
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
|
||||
*/
|
||||
protected function findExistingAccount(array $validTypes, array $data, bool $inverse = false): ?Account
|
||||
@@ -284,6 +266,7 @@ class AccountValidator
|
||||
$check = $inverse ? !$check : $check; // reverse the validation check if necessary.
|
||||
if ((null !== $first) && $check) {
|
||||
app('log')->debug(sprintf('ID: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
|
||||
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
@@ -296,6 +279,7 @@ class AccountValidator
|
||||
$check = $inverse ? !$check : $check; // reverse the validation check if necessary.
|
||||
if ((null !== $first) && $check) {
|
||||
app('log')->debug(sprintf('Iban: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
|
||||
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
@@ -308,6 +292,7 @@ class AccountValidator
|
||||
$check = $inverse ? !$check : $check; // reverse the validation check if necessary.
|
||||
if ((null !== $first) && $check) {
|
||||
app('log')->debug(sprintf('Number: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
|
||||
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
@@ -317,6 +302,7 @@ class AccountValidator
|
||||
$first = $this->getRepository()->findByName($accountName, $validTypes);
|
||||
if (null !== $first) {
|
||||
app('log')->debug(sprintf('Name: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
|
||||
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
@@ -325,10 +311,7 @@ class AccountValidator
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AccountRepositoryInterface|UserGroupAccountRepositoryInterface
|
||||
*/
|
||||
private function getRepository(): AccountRepositoryInterface | UserGroupAccountRepositoryInterface
|
||||
private function getRepository(): AccountRepositoryInterface|UserGroupAccountRepositoryInterface
|
||||
{
|
||||
if ($this->useUserGroupRepository) {
|
||||
return $this->userGroupAccountRepository;
|
||||
|
||||
@@ -26,17 +26,11 @@ namespace FireflyIII\Validation\Api\Data\Bulk;
|
||||
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Illuminate\Validation\Validator;
|
||||
use JsonException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
trait ValidatesBulkTransactionQuery
|
||||
{
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @throws JsonException
|
||||
* @throws \JsonException
|
||||
*/
|
||||
protected function validateTransactionQuery(Validator $validator): void
|
||||
{
|
||||
@@ -73,8 +67,8 @@ trait ValidatesBulkTransactionQuery
|
||||
$sourceCurrency = $repository->getAccountCurrency($source);
|
||||
$destCurrency = $repository->getAccountCurrency($dest);
|
||||
if (
|
||||
$sourceCurrency !== null
|
||||
&& $destCurrency !== null
|
||||
null !== $sourceCurrency
|
||||
&& null !== $destCurrency
|
||||
&& $sourceCurrency->id !== $destCurrency->id
|
||||
) {
|
||||
$validator->errors()->add('query', (string)trans('validation.invalid_query_currency'));
|
||||
|
||||
@@ -30,9 +30,6 @@ use Illuminate\Validation\Validator;
|
||||
*/
|
||||
trait ValidatesAutoBudgetRequest
|
||||
{
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function validateAutoBudgetAmount(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
@@ -66,6 +63,7 @@ trait ValidatesAutoBudgetRequest
|
||||
// too big amount
|
||||
if ((int)$amount > 268435456) {
|
||||
$validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget'));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ trait CurrencyValidation
|
||||
|
||||
/**
|
||||
* If the transactions contain foreign amounts, there must also be foreign currency information.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function validateForeignCurrencyInformation(Validator $validator): void
|
||||
{
|
||||
@@ -62,7 +60,7 @@ trait CurrencyValidation
|
||||
&& 0 !== bccomp('0', $transaction['foreign_amount'])
|
||||
) {
|
||||
$validator->errors()->add(
|
||||
'transactions.' . $index . '.foreign_amount',
|
||||
'transactions.'.$index.'.foreign_amount',
|
||||
(string)trans('validation.require_currency_info')
|
||||
);
|
||||
}
|
||||
@@ -73,17 +71,12 @@ trait CurrencyValidation
|
||||
$transaction
|
||||
)) {
|
||||
$validator->errors()->add(
|
||||
'transactions.' . $index . '.foreign_amount',
|
||||
'transactions.'.$index.'.foreign_amount',
|
||||
(string)trans('validation.require_currency_amount')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getTransactionsArray(Validator $validator): array;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Validation;
|
||||
|
||||
use Config;
|
||||
use DB;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
@@ -37,14 +35,11 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Services\Password\Verifier;
|
||||
use FireflyIII\Support\ParseDateString;
|
||||
use FireflyIII\TransactionRules\Triggers\TriggerInterface;
|
||||
use FireflyIII\User;
|
||||
use Google2FA;
|
||||
use Illuminate\Validation\Validator;
|
||||
use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException;
|
||||
use PragmaRX\Google2FA\Exceptions\InvalidCharactersException;
|
||||
use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException;
|
||||
use ValueError;
|
||||
|
||||
/**
|
||||
* Class FireflyValidator.
|
||||
@@ -56,10 +51,10 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
* @throws IncompatibleWithGoogleAuthenticatorException
|
||||
* @throws InvalidCharactersException
|
||||
* @throws SecretKeyTooShortException
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validate2faCode($attribute, $value): bool
|
||||
@@ -70,6 +65,7 @@ class FireflyValidator extends Validator
|
||||
$user = auth()->user();
|
||||
if (null === $user) {
|
||||
app('log')->error('No user during validate2faCode');
|
||||
|
||||
return false;
|
||||
}
|
||||
$secretPreference = app('preferences')->get('temp-mfa-secret');
|
||||
@@ -78,16 +74,15 @@ class FireflyValidator extends Validator
|
||||
$secret = '';
|
||||
}
|
||||
|
||||
return (bool)Google2FA::verifyKey((string)$secret, $value);
|
||||
return (bool)\Google2FA::verifyKey((string)$secret, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateBelongsToUser($attribute, $value, $parameters): bool
|
||||
{
|
||||
@@ -96,7 +91,7 @@ class FireflyValidator extends Validator
|
||||
if (0 === (int)$value) {
|
||||
return true;
|
||||
}
|
||||
$count = DB::table($parameters[0])->where('user_id', auth()->user()->id)->where($field, $value)->count();
|
||||
$count = \DB::table($parameters[0])->where('user_id', auth()->user()->id)->where($field, $value)->count();
|
||||
|
||||
return 1 === $count;
|
||||
}
|
||||
@@ -104,9 +99,8 @@ class FireflyValidator extends Validator
|
||||
/**
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateBic($attribute, $value): bool
|
||||
{
|
||||
@@ -123,11 +117,7 @@ class FireflyValidator extends Validator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateIban(mixed $attribute, mixed $value): bool
|
||||
{
|
||||
@@ -225,14 +215,15 @@ class FireflyValidator extends Validator
|
||||
// take
|
||||
$first = substr($value, 0, 4);
|
||||
$last = substr($value, 4);
|
||||
$iban = $last . $first;
|
||||
$iban = $last.$first;
|
||||
$iban = trim(str_replace($search, $replace, $iban));
|
||||
if ('' === $iban) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$checksum = bcmod($iban, '97');
|
||||
} catch (ValueError $e) { // @phpstan-ignore-line
|
||||
} catch (\ValueError $e) { // @phpstan-ignore-line
|
||||
$message = sprintf('Could not validate IBAN check value "%s" (IBAN "%s")', $iban, $value);
|
||||
app('log')->error($message);
|
||||
app('log')->error($e->getTraceAsString());
|
||||
@@ -247,9 +238,8 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateLess($attribute, $value, $parameters): bool
|
||||
{
|
||||
@@ -263,9 +253,8 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateMore($attribute, $value, $parameters): bool
|
||||
{
|
||||
@@ -279,9 +268,8 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateMustExist($attribute, $value, $parameters): bool
|
||||
{
|
||||
@@ -290,18 +278,11 @@ class FireflyValidator extends Validator
|
||||
if (0 === (int)$value) {
|
||||
return true;
|
||||
}
|
||||
$count = DB::table($parameters[0])->where($field, $value)->count();
|
||||
$count = \DB::table($parameters[0])->where($field, $value)->count();
|
||||
|
||||
return 1 === $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
*
|
||||
* @param string|null $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateRuleActionValue(string $attribute, string $value = null): bool
|
||||
{
|
||||
// first, get the index from this string:
|
||||
@@ -367,11 +348,6 @@ class FireflyValidator extends Validator
|
||||
|
||||
/**
|
||||
* $attribute has the format triggers.%d.value.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string|null $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateRuleTriggerValue(string $attribute, string $value = null): bool
|
||||
{
|
||||
@@ -436,6 +412,7 @@ class FireflyValidator extends Validator
|
||||
if (in_array($triggerType, ['date_is', 'created_on', 'updated_on', 'date_before', 'date_after'], true)) {
|
||||
/** @var ParseDateString $parser */
|
||||
$parser = app(ParseDateString::class);
|
||||
|
||||
try {
|
||||
$parser->parseDate($value);
|
||||
} catch (FireflyException $e) {
|
||||
@@ -451,9 +428,8 @@ class FireflyValidator extends Validator
|
||||
/**
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateSecurePassword($attribute, $value): bool
|
||||
{
|
||||
@@ -475,167 +451,56 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateUniqueAccountForUser($attribute, $value, $parameters): bool
|
||||
{
|
||||
// because a user does not have to be logged in (tests and what-not).
|
||||
if (!auth()->check()) {
|
||||
app('log')->debug('validateUniqueAccountForUser::anon');
|
||||
|
||||
return $this->validateAccountAnonymously();
|
||||
}
|
||||
if (array_key_exists('objectType', $this->data)) {
|
||||
app('log')->debug('validateUniqueAccountForUser::typeString');
|
||||
|
||||
return $this->validateByAccountTypeString($value, $parameters, $this->data['objectType']);
|
||||
}
|
||||
if (array_key_exists('type', $this->data)) {
|
||||
app('log')->debug('validateUniqueAccountForUser::typeString');
|
||||
|
||||
return $this->validateByAccountTypeString($value, $parameters, (string)$this->data['type']);
|
||||
}
|
||||
if (array_key_exists('account_type_id', $this->data)) {
|
||||
app('log')->debug('validateUniqueAccountForUser::typeId');
|
||||
|
||||
return $this->validateByAccountTypeId($value, $parameters);
|
||||
}
|
||||
$parameterId = $parameters[0] ?? null;
|
||||
if (null !== $parameterId) {
|
||||
app('log')->debug('validateUniqueAccountForUser::paramId');
|
||||
|
||||
return $this->validateByParameterId((int)$parameterId, $value);
|
||||
}
|
||||
if (array_key_exists('id', $this->data)) {
|
||||
app('log')->debug('validateUniqueAccountForUser::accountId');
|
||||
|
||||
return $this->validateByAccountId($value);
|
||||
}
|
||||
|
||||
// without type, just try to validate the name.
|
||||
app('log')->debug('validateUniqueAccountForUser::accountName');
|
||||
|
||||
return $this->validateByAccountName($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function validateAccountAnonymously(): bool
|
||||
{
|
||||
if (!array_key_exists('user_id', $this->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::find($this->data['user_id']);
|
||||
$type = AccountType::find($this->data['account_type_id'])->first();
|
||||
$value = $this->data['name'];
|
||||
|
||||
/** @var Account|null $result */
|
||||
$result = $user->accounts()->where('account_type_id', $type->id)->where('name', $value)->first();
|
||||
|
||||
return null === $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param array $parameters
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validateByAccountTypeString(string $value, array $parameters, string $type): bool
|
||||
{
|
||||
/** @var array|null $search */
|
||||
$search = Config::get('firefly.accountTypeByIdentifier.' . $type);
|
||||
|
||||
if (null === $search) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$accountTypes = AccountType::whereIn('type', $search)->get();
|
||||
$ignore = (int)($parameters[0] ?? 0.0);
|
||||
$accountTypeIds = $accountTypes->pluck('id')->toArray();
|
||||
/** @var Account|null $result */
|
||||
$result = auth()->user()->accounts()->whereIn('account_type_id', $accountTypeIds)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first();
|
||||
return null === $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validateByAccountTypeId($value, $parameters): bool
|
||||
{
|
||||
$type = AccountType::find($this->data['account_type_id'])->first();
|
||||
$ignore = (int)($parameters[0] ?? 0.0);
|
||||
|
||||
/** @var Account|null $result */
|
||||
$result = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first();
|
||||
|
||||
return null === $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountId
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validateByParameterId(int $accountId, $value): bool
|
||||
{
|
||||
/** @var Account $existingAccount */
|
||||
$existingAccount = Account::find($accountId);
|
||||
|
||||
$type = $existingAccount->accountType;
|
||||
$ignore = $existingAccount->id;
|
||||
|
||||
$entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first();
|
||||
|
||||
return null === $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validateByAccountId($value): bool
|
||||
{
|
||||
/** @var Account $existingAccount */
|
||||
$existingAccount = Account::find($this->data['id']);
|
||||
|
||||
$type = $existingAccount->accountType;
|
||||
$ignore = $existingAccount->id;
|
||||
|
||||
$entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first();
|
||||
|
||||
return null === $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validateByAccountName(string $value): bool
|
||||
{
|
||||
return auth()->user()->accounts()->where('name', $value)->count() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateUniqueAccountNumberForUser($attribute, $value, $parameters): bool
|
||||
{
|
||||
@@ -645,10 +510,11 @@ class FireflyValidator extends Validator
|
||||
}
|
||||
|
||||
$query = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id')
|
||||
->whereNull('accounts.deleted_at')
|
||||
->where('accounts.user_id', auth()->user()->id)
|
||||
->where('account_meta.name', 'account_number')
|
||||
->where('account_meta.data', json_encode($value));
|
||||
->whereNull('accounts.deleted_at')
|
||||
->where('accounts.user_id', auth()->user()->id)
|
||||
->where('account_meta.name', 'account_number')
|
||||
->where('account_meta.data', json_encode($value))
|
||||
;
|
||||
|
||||
if ($accountId > 0) {
|
||||
// exclude current account from check.
|
||||
@@ -666,9 +532,11 @@ class FireflyValidator extends Validator
|
||||
$type = $this->data['objectType'] ?? 'unknown';
|
||||
if ('expense' !== $type && 'revenue' !== $type) {
|
||||
app('log')->warning(sprintf('Account number "%s" is not unique and account type "%s" cannot share its account number.', $value, $type));
|
||||
|
||||
return false;
|
||||
}
|
||||
app('log')->debug(sprintf('Account number "%s" is not unique but account type "%s" may share its account number.', $value, $type));
|
||||
|
||||
// one other account with this account number.
|
||||
/** @var AccountMeta $entry */
|
||||
foreach ($set as $entry) {
|
||||
@@ -676,56 +544,37 @@ class FireflyValidator extends Validator
|
||||
$otherType = (string)config(sprintf('firefly.shortNamesByFullName.%s', $otherAccount->accountType->type));
|
||||
if (('expense' === $otherType || 'revenue' === $otherType) && $otherType !== $type) {
|
||||
app('log')->debug(sprintf('The other account with this account number is a "%s" so return true.', $otherType));
|
||||
|
||||
return true;
|
||||
}
|
||||
app('log')->debug(sprintf('The other account with this account number is a "%s" so return false.', $otherType));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $attribute
|
||||
* @param string|null $value
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateUniqueCurrencyCode(string | null $attribute, string | null $value): bool
|
||||
public function validateUniqueCurrencyCode(null|string $attribute, null|string $value): bool
|
||||
{
|
||||
return $this->validateUniqueCurrency('code', (string)$attribute, (string)$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param string $attribute
|
||||
* @param string $value
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateUniqueCurrency(string $field, string $attribute, string $value): bool
|
||||
{
|
||||
return 0 === DB::table('transaction_currencies')->where($field, $value)->whereNull('deleted_at')->count();
|
||||
return 0 === \DB::table('transaction_currencies')->where($field, $value)->whereNull('deleted_at')->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $attribute
|
||||
* @param string|null $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateUniqueCurrencyName(string | null $attribute, string | null $value): bool
|
||||
public function validateUniqueCurrencyName(null|string $attribute, null|string $value): bool
|
||||
{
|
||||
return $this->validateUniqueCurrency('name', (string)$attribute, (string)$value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $attribute
|
||||
* @param string|null $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateUniqueCurrencySymbol(string | null $attribute, string | null $value): bool
|
||||
public function validateUniqueCurrencySymbol(null|string $attribute, null|string $value): bool
|
||||
{
|
||||
return $this->validateUniqueCurrency('symbol', (string)$attribute, (string)$value);
|
||||
}
|
||||
@@ -734,9 +583,8 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @param mixed $something
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateUniqueExistingWebhook($value, $parameters, $something): bool
|
||||
{
|
||||
@@ -750,7 +598,7 @@ class FireflyValidator extends Validator
|
||||
if (auth()->check()) {
|
||||
// get existing webhook value:
|
||||
if (0 !== $existingId) {
|
||||
/** @var Webhook|null $webhook */
|
||||
/** @var null|Webhook $webhook */
|
||||
$webhook = auth()->user()->webhooks()->find($existingId);
|
||||
if (null === $webhook) {
|
||||
return false;
|
||||
@@ -769,18 +617,18 @@ class FireflyValidator extends Validator
|
||||
$userId = auth()->user()->id;
|
||||
|
||||
return 0 === Webhook::whereUserId($userId)
|
||||
->where('trigger', $trigger)
|
||||
->where('response', $response)
|
||||
->where('delivery', $delivery)
|
||||
->where('id', '!=', $existingId)
|
||||
->where('url', $url)->count();
|
||||
->where('trigger', $trigger)
|
||||
->where('response', $response)
|
||||
->where('delivery', $delivery)
|
||||
->where('id', '!=', $existingId)
|
||||
->where('url', $url)->count()
|
||||
;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Validate an object and its uniqueness. Checks for encryption / encrypted values as well.
|
||||
*
|
||||
* parameter 0: the table
|
||||
@@ -790,9 +638,8 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateUniqueObjectForUser($attribute, $value, $parameters): bool
|
||||
{
|
||||
@@ -808,13 +655,15 @@ class FireflyValidator extends Validator
|
||||
$exclude = (int)$data['id'];
|
||||
}
|
||||
// get entries from table
|
||||
$result = DB::table($table)->where('user_id', auth()->user()->id)->whereNull('deleted_at')
|
||||
->where('id', '!=', $exclude)
|
||||
->where($field, $value)
|
||||
->first([$field]);
|
||||
$result = \DB::table($table)->where('user_id', auth()->user()->id)->whereNull('deleted_at')
|
||||
->where('id', '!=', $exclude)
|
||||
->where($field, $value)
|
||||
->first([$field])
|
||||
;
|
||||
if (null === $result) {
|
||||
return true; // not found, so true.
|
||||
}
|
||||
|
||||
// found, so not unique.
|
||||
return false;
|
||||
}
|
||||
@@ -823,17 +672,17 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateUniqueObjectGroup($attribute, $value, $parameters): bool
|
||||
{
|
||||
$exclude = $parameters[0] ?? null;
|
||||
$query = DB::table('object_groups')
|
||||
->whereNull('object_groups.deleted_at')
|
||||
->where('object_groups.user_id', auth()->user()->id)
|
||||
->where('object_groups.title', $value);
|
||||
$query = \DB::table('object_groups')
|
||||
->whereNull('object_groups.deleted_at')
|
||||
->where('object_groups.user_id', auth()->user()->id)
|
||||
->where('object_groups.title', $value)
|
||||
;
|
||||
if (null !== $exclude) {
|
||||
$query->where('object_groups.id', '!=', (int)$exclude);
|
||||
}
|
||||
@@ -845,15 +694,15 @@ class FireflyValidator extends Validator
|
||||
* @param mixed $attribute
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateUniquePiggyBankForUser($attribute, $value, $parameters): bool
|
||||
{
|
||||
$exclude = $parameters[0] ?? null;
|
||||
$query = DB::table('piggy_banks')->whereNull('piggy_banks.deleted_at')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', auth()->user()->id);
|
||||
$query = \DB::table('piggy_banks')->whereNull('piggy_banks.deleted_at')
|
||||
->leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->where('accounts.user_id', auth()->user()->id)
|
||||
;
|
||||
if (null !== $exclude) {
|
||||
$query->where('piggy_banks.id', '!=', (int)$exclude);
|
||||
}
|
||||
@@ -865,9 +714,8 @@ class FireflyValidator extends Validator
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*
|
||||
* @return bool
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validateUniqueWebhook($value, $parameters): bool
|
||||
{
|
||||
@@ -884,12 +732,113 @@ class FireflyValidator extends Validator
|
||||
$userId = auth()->user()->id;
|
||||
|
||||
return 0 === Webhook::whereUserId($userId)
|
||||
->where('trigger', $trigger)
|
||||
->where('response', $response)
|
||||
->where('delivery', $delivery)
|
||||
->where('url', $url)->count();
|
||||
->where('trigger', $trigger)
|
||||
->where('response', $response)
|
||||
->where('delivery', $delivery)
|
||||
->where('url', $url)->count()
|
||||
;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function validateAccountAnonymously(): bool
|
||||
{
|
||||
if (!array_key_exists('user_id', $this->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::find($this->data['user_id']);
|
||||
$type = AccountType::find($this->data['account_type_id'])->first();
|
||||
$value = $this->data['name'];
|
||||
|
||||
/** @var null|Account $result */
|
||||
$result = $user->accounts()->where('account_type_id', $type->id)->where('name', $value)->first();
|
||||
|
||||
return null === $result;
|
||||
}
|
||||
|
||||
private function validateByAccountTypeString(string $value, array $parameters, string $type): bool
|
||||
{
|
||||
/** @var null|array $search */
|
||||
$search = \Config::get('firefly.accountTypeByIdentifier.'.$type);
|
||||
|
||||
if (null === $search) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$accountTypes = AccountType::whereIn('type', $search)->get();
|
||||
$ignore = (int)($parameters[0] ?? 0.0);
|
||||
$accountTypeIds = $accountTypes->pluck('id')->toArray();
|
||||
|
||||
/** @var null|Account $result */
|
||||
$result = auth()->user()->accounts()->whereIn('account_type_id', $accountTypeIds)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first()
|
||||
;
|
||||
|
||||
return null === $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $parameters
|
||||
*/
|
||||
private function validateByAccountTypeId($value, $parameters): bool
|
||||
{
|
||||
$type = AccountType::find($this->data['account_type_id'])->first();
|
||||
$ignore = (int)($parameters[0] ?? 0.0);
|
||||
|
||||
/** @var null|Account $result */
|
||||
$result = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first()
|
||||
;
|
||||
|
||||
return null === $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function validateByParameterId(int $accountId, $value): bool
|
||||
{
|
||||
/** @var Account $existingAccount */
|
||||
$existingAccount = Account::find($accountId);
|
||||
|
||||
$type = $existingAccount->accountType;
|
||||
$ignore = $existingAccount->id;
|
||||
|
||||
$entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first()
|
||||
;
|
||||
|
||||
return null === $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function validateByAccountId($value): bool
|
||||
{
|
||||
/** @var Account $existingAccount */
|
||||
$existingAccount = Account::find($this->data['id']);
|
||||
|
||||
$type = $existingAccount->accountType;
|
||||
$ignore = $existingAccount->id;
|
||||
|
||||
$entry = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)
|
||||
->where('name', $value)
|
||||
->first()
|
||||
;
|
||||
|
||||
return null === $entry;
|
||||
}
|
||||
|
||||
private function validateByAccountName(string $value): bool
|
||||
{
|
||||
return 0 === auth()->user()->accounts()->where('name', $value)->count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,6 @@ trait GroupValidation
|
||||
* TODO This should prevent errors down the road but I'm not yet sure what I'm validating here
|
||||
* TODO so I disabled this on 2023-10-22 to see if it causes any issues.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function preventNoAccountInfo(Validator $validator): void
|
||||
@@ -59,7 +57,8 @@ trait GroupValidation
|
||||
'source_number',
|
||||
'destination_number',
|
||||
];
|
||||
/** @var array|null $transaction */
|
||||
|
||||
/** @var null|array $transaction */
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
if (!is_array($transaction)) {
|
||||
throw new FireflyException('Invalid data submitted: transaction is not array.');
|
||||
@@ -86,35 +85,26 @@ trait GroupValidation
|
||||
// only an issue if there is no transaction_journal_id
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getTransactionsArray(Validator $validator): array;
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function preventUpdateReconciled(Validator $validator, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$count = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_groups', 'transaction_groups.id', 'transaction_journals.transaction_group_id')
|
||||
->where('transaction_journals.transaction_group_id', $transactionGroup->id)
|
||||
->where('transactions.reconciled', 1)->where('transactions.amount', '<', 0)->count('transactions.id');
|
||||
->leftJoin('transaction_groups', 'transaction_groups.id', 'transaction_journals.transaction_group_id')
|
||||
->where('transaction_journals.transaction_group_id', $transactionGroup->id)
|
||||
->where('transactions.reconciled', 1)->where('transactions.amount', '<', 0)->count('transactions.id')
|
||||
;
|
||||
if (0 === $count) {
|
||||
app('log')->debug(sprintf('Transaction is not reconciled, done with %s', __METHOD__));
|
||||
|
||||
return;
|
||||
}
|
||||
$data = $validator->getData();
|
||||
$forbidden = ['amount', 'foreign_amount', 'currency_code', 'currency_id', 'foreign_currency_code', 'foreign_currency_id',
|
||||
'source_id', 'source_name', 'source_number', 'source_iban',
|
||||
'destination_id', 'destination_name', 'destination_number', 'destination_iban',
|
||||
'source_id', 'source_name', 'source_number', 'source_iban',
|
||||
'destination_id', 'destination_name', 'destination_number', 'destination_iban',
|
||||
];
|
||||
foreach ($data['transactions'] as $index => $row) {
|
||||
foreach ($forbidden as $key) {
|
||||
@@ -133,8 +123,6 @@ trait GroupValidation
|
||||
/**
|
||||
* Adds an error to the "description" field when the user has submitted no descriptions and no
|
||||
* journal description.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function validateDescriptions(Validator $validator): void
|
||||
{
|
||||
@@ -146,7 +134,7 @@ trait GroupValidation
|
||||
$validDescriptions = 0;
|
||||
foreach ($transactions as $transaction) {
|
||||
if ('' !== (string)($transaction['description'] ?? null)) {
|
||||
$validDescriptions++;
|
||||
++$validDescriptions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,9 +147,6 @@ trait GroupValidation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function validateGroupDescription(Validator $validator): void
|
||||
{
|
||||
if ($validator->errors()->count() > 0) {
|
||||
@@ -181,9 +166,6 @@ trait GroupValidation
|
||||
* This method validates if the user has submitted transaction journal ID's for each array they submit, if they've
|
||||
* submitted more than 1 transaction journal. This check is necessary because Firefly III isn't able to distinguish
|
||||
* between journals without the ID.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*/
|
||||
protected function validateJournalIds(Validator $validator, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
@@ -196,6 +178,7 @@ trait GroupValidation
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check each array:
|
||||
/**
|
||||
* @var int $index
|
||||
@@ -208,12 +191,6 @@ trait GroupValidation
|
||||
|
||||
/**
|
||||
* Do the validation required by validateJournalIds.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param array $transaction
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*
|
||||
*/
|
||||
private function validateJournalId(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
|
||||
@@ -27,13 +27,11 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\RecurrenceTransaction;
|
||||
use Illuminate\Validation\Validator;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Trait RecurrenceValidation
|
||||
*
|
||||
* Contains advanced validation rules used in validation of new and existing recurrences.
|
||||
*
|
||||
*/
|
||||
trait RecurrenceValidation
|
||||
{
|
||||
@@ -41,8 +39,6 @@ trait RecurrenceValidation
|
||||
* Validate account information input for recurrences which are being updated.
|
||||
*
|
||||
* TODO Must always trigger when the type of the recurrence changes.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function valUpdateAccountInfo(Validator $validator): void
|
||||
{
|
||||
@@ -53,12 +49,14 @@ trait RecurrenceValidation
|
||||
// grab model from parameter and try to set the transaction type from it
|
||||
if ('invalid' === $transactionType) {
|
||||
app('log')->debug('Type is invalid but we will search for it.');
|
||||
/** @var Recurrence|null $recurrence */
|
||||
|
||||
/** @var null|Recurrence $recurrence */
|
||||
$recurrence = $this->route()?->parameter('recurrence');
|
||||
if (null !== $recurrence) {
|
||||
app('log')->debug('There is a recurrence in the route.');
|
||||
|
||||
// ok so we have a recurrence should be able to extract type somehow.
|
||||
/** @var RecurrenceTransaction|null $first */
|
||||
/** @var null|RecurrenceTransaction $first */
|
||||
$first = $recurrence->recurrenceTransactions()->first();
|
||||
if (null !== $first) {
|
||||
$transactionType = null !== $first->transactionType ? $first->transactionType->type : 'withdrawal';
|
||||
@@ -104,7 +102,7 @@ trait RecurrenceValidation
|
||||
// validate destination account
|
||||
$destinationId = array_key_exists('destination_id', $transaction) ? (int)$transaction['destination_id'] : null;
|
||||
$destinationName = $transaction['destination_name'] ?? null;
|
||||
$validDestination = $accountValidator->validateDestination(['id' => $destinationId, 'name' => $destinationName,]);
|
||||
$validDestination = $accountValidator->validateDestination(['id' => $destinationId, 'name' => $destinationName]);
|
||||
// do something with result:
|
||||
if (false === $validDestination) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.destination_id', $index), $accountValidator->destError);
|
||||
@@ -117,8 +115,6 @@ trait RecurrenceValidation
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateOneRepetition(Validator $validator): void
|
||||
{
|
||||
@@ -132,8 +128,6 @@ trait RecurrenceValidation
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no repetitions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateOneRepetitionUpdate(Validator $validator): void
|
||||
{
|
||||
@@ -151,8 +145,6 @@ trait RecurrenceValidation
|
||||
/**
|
||||
* Validates that the recurrence has valid repetition information. It either doesn't stop,
|
||||
* or stops after X times or at X date. Not both of them.,
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateRecurrenceRepetition(Validator $validator): void
|
||||
{
|
||||
@@ -166,11 +158,6 @@ trait RecurrenceValidation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function validateRecurringConfig(Validator $validator)
|
||||
{
|
||||
$data = $validator->getData();
|
||||
@@ -189,9 +176,6 @@ trait RecurrenceValidation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateRepetitionMoment(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
@@ -201,6 +185,7 @@ trait RecurrenceValidation
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var int $index
|
||||
* @var array $repetition
|
||||
@@ -218,20 +203,30 @@ trait RecurrenceValidation
|
||||
$validator->errors()->add(sprintf('repetitions.%d.type', $index), (string)trans('validation.valid_recurrence_rep_type'));
|
||||
|
||||
return;
|
||||
|
||||
case 'daily':
|
||||
$this->validateDaily($validator, $index, (string)$repetition['moment']);
|
||||
|
||||
break;
|
||||
|
||||
case 'monthly':
|
||||
$this->validateMonthly($validator, $index, (int)$repetition['moment']);
|
||||
|
||||
break;
|
||||
|
||||
case 'ndom':
|
||||
$this->validateNdom($validator, $index, (string)$repetition['moment']);
|
||||
|
||||
break;
|
||||
|
||||
case 'weekly':
|
||||
$this->validateWeekly($validator, $index, (int)$repetition['moment']);
|
||||
|
||||
break;
|
||||
|
||||
case 'yearly':
|
||||
$this->validateYearly($validator, $index, (string)$repetition['moment']);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -239,10 +234,6 @@ trait RecurrenceValidation
|
||||
|
||||
/**
|
||||
* If the repetition type is daily, the moment should be empty.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param string $moment
|
||||
*/
|
||||
protected function validateDaily(Validator $validator, int $index, string $moment): void
|
||||
{
|
||||
@@ -253,10 +244,6 @@ trait RecurrenceValidation
|
||||
|
||||
/**
|
||||
* If the repetition type is monthly, the moment should be a day between 1-31 (inclusive).
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param int $dayOfMonth
|
||||
*/
|
||||
protected function validateMonthly(Validator $validator, int $index, int $dayOfMonth): void
|
||||
{
|
||||
@@ -268,10 +255,6 @@ trait RecurrenceValidation
|
||||
/**
|
||||
* If the repetition type is "ndom", the first part must be between 1-5 (inclusive), for the week in the month,
|
||||
* and the second one must be between 1-7 (inclusive) for the day of the week.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param string $moment
|
||||
*/
|
||||
protected function validateNdom(Validator $validator, int $index, string $moment): void
|
||||
{
|
||||
@@ -295,10 +278,6 @@ trait RecurrenceValidation
|
||||
|
||||
/**
|
||||
* If the repetition type is weekly, the moment should be a day between 1-7 (inclusive).
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param int $dayOfWeek
|
||||
*/
|
||||
protected function validateWeekly(Validator $validator, int $index, int $dayOfWeek): void
|
||||
{
|
||||
@@ -309,27 +288,17 @@ trait RecurrenceValidation
|
||||
|
||||
/**
|
||||
* If the repetition type is yearly, the moment should be a valid date.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param string $moment
|
||||
*/
|
||||
protected function validateYearly(Validator $validator, int $index, string $moment): void
|
||||
{
|
||||
try {
|
||||
Carbon::createFromFormat('Y-m-d', $moment);
|
||||
} catch (InvalidArgumentException $e) { // @phpstan-ignore-line
|
||||
} catch (\InvalidArgumentException $e) { // @phpstan-ignore-line
|
||||
app('log')->debug(sprintf('Invalid argument for Carbon: %s', $e->getMessage()));
|
||||
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), (string)trans('validation.valid_recurrence_rep_moment'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Recurrence $recurrence
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function validateTransactionId(Recurrence $recurrence, Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in validateTransactionId');
|
||||
@@ -339,6 +308,7 @@ trait RecurrenceValidation
|
||||
if (0 === $submittedTrCount) {
|
||||
app('log')->warning('[b] User submitted no transactions.');
|
||||
$validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction'));
|
||||
|
||||
return;
|
||||
}
|
||||
$originalTrCount = $recurrence->recurrenceTransactions()->count();
|
||||
@@ -346,11 +316,13 @@ trait RecurrenceValidation
|
||||
$first = $transactions[0]; // can safely assume index 0.
|
||||
if (!array_key_exists('id', $first)) {
|
||||
app('log')->debug('Single count and no ID, done.');
|
||||
|
||||
return; // home safe!
|
||||
}
|
||||
$id = $first['id'];
|
||||
if ('' === (string)$id) {
|
||||
app('log')->debug('Single count and empty ID, done.');
|
||||
|
||||
return; // home safe!
|
||||
}
|
||||
$integer = (int)$id;
|
||||
@@ -360,6 +332,7 @@ trait RecurrenceValidation
|
||||
$validator->errors()->add('transactions.0.id', (string)trans('validation.id_does_not_match', ['id' => $integer]));
|
||||
}
|
||||
app('log')->debug('Single ID validation done.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -369,6 +342,7 @@ trait RecurrenceValidation
|
||||
app('log')->debug(sprintf('User submits %d transaction, recurrence has %d transactions. All entries must have ID.', $submittedTrCount, $originalTrCount));
|
||||
$idsMandatory = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop all transactions submitted by the user.
|
||||
* If the user has submitted fewer transactions than the original recurrence has, all submitted entries must have an ID.
|
||||
@@ -387,11 +361,13 @@ trait RecurrenceValidation
|
||||
if (!is_array($transaction)) {
|
||||
app('log')->warning('Not an array. Give error.');
|
||||
$validator->errors()->add(sprintf('transactions.%d.id', $index), (string)trans('validation.at_least_one_transaction'));
|
||||
|
||||
return;
|
||||
}
|
||||
if (!array_key_exists('id', $transaction) && $idsMandatory) {
|
||||
app('log')->warning('ID is mandatory but array has no ID.');
|
||||
$validator->errors()->add(sprintf('transactions.%d.id', $index), (string)trans('validation.need_id_to_match'));
|
||||
|
||||
return;
|
||||
}
|
||||
if (array_key_exists('id', $transaction)) { // don't matter if $idsMandatory
|
||||
@@ -399,12 +375,12 @@ trait RecurrenceValidation
|
||||
$idCount = $recurrence->recurrenceTransactions()->where('recurrences_transactions.id', (int)$transaction['id'])->count();
|
||||
if (0 === $idCount) {
|
||||
app('log')->debug('ID does not exist or no match. Count another unmatched ID.');
|
||||
$unmatchedIds++;
|
||||
++$unmatchedIds;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists('id', $transaction) && !$idsMandatory) {
|
||||
app('log')->debug('Array has no ID but was not mandatory at this point.');
|
||||
$unmatchedIds++;
|
||||
++$unmatchedIds;
|
||||
}
|
||||
}
|
||||
// if too many don't match, but you haven't submitted more than already present:
|
||||
@@ -413,6 +389,7 @@ trait RecurrenceValidation
|
||||
if ($unmatchedIds > $maxUnmatched) {
|
||||
app('log')->warning(sprintf('Too many unmatched transactions (%d).', $unmatchedIds));
|
||||
$validator->errors()->add('transactions.0.id', (string)trans('validation.too_many_unmatched'));
|
||||
|
||||
return;
|
||||
}
|
||||
app('log')->debug('Done with ID validation.');
|
||||
|
||||
@@ -44,10 +44,6 @@ trait TransactionValidation
|
||||
* Validates the given account information. Switches on given transaction type.
|
||||
*
|
||||
* Inclusion of user and/or group is optional.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param User|null $user
|
||||
* @param UserGroup|null $userGroup
|
||||
*/
|
||||
public function validateAccountInformation(Validator $validator, User $user = null, UserGroup $userGroup = null): void
|
||||
{
|
||||
@@ -60,8 +56,9 @@ trait TransactionValidation
|
||||
$transactionType = $data['type'] ?? 'invalid';
|
||||
|
||||
app('log')->debug(sprintf('Going to loop %d transaction(s)', count($transactions)));
|
||||
|
||||
/**
|
||||
* @var int|null $index
|
||||
* @var null|int $index
|
||||
* @var array $transaction
|
||||
*/
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
@@ -75,10 +72,135 @@ trait TransactionValidation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* Validates the given account information. Switches on given transaction type.
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function validateAccountInformationUpdate(Validator $validator, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
app('log')->debug('Now in validateAccountInformationUpdate()');
|
||||
if ($validator->errors()->count() > 0) {
|
||||
app('log')->debug('Validator already has errors, so return.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
|
||||
/**
|
||||
* @var null|int $index
|
||||
* @var array $transaction
|
||||
*/
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
if (!is_int($index)) {
|
||||
throw new FireflyException('Invalid data submitted: transaction is not array.');
|
||||
}
|
||||
$this->validateSingleUpdate($validator, $index, $transaction, $transactionGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no transactions in the array of data.
|
||||
*/
|
||||
public function validateOneRecurrenceTransaction(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in validateOneRecurrenceTransaction()');
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
|
||||
// need at least one transaction
|
||||
if (0 === count($transactions)) {
|
||||
$validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no transactions in the array of data.
|
||||
*/
|
||||
public function validateOneTransaction(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in validateOneTransaction');
|
||||
if ($validator->errors()->count() > 0) {
|
||||
app('log')->debug('Validator already has errors, so return.');
|
||||
|
||||
return;
|
||||
}
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
// need at least one transaction
|
||||
if (0 === count($transactions)) {
|
||||
$validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction'));
|
||||
app('log')->debug('Added error: at_least_one_transaction.');
|
||||
|
||||
return;
|
||||
}
|
||||
app('log')->debug('Added NO errors.');
|
||||
}
|
||||
|
||||
public function validateTransactionArray(Validator $validator): void
|
||||
{
|
||||
if ($validator->errors()->count() > 0) {
|
||||
return;
|
||||
}
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
foreach (array_keys($transactions) as $key) {
|
||||
if (!is_int($key)) {
|
||||
$validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction'));
|
||||
app('log')->debug('Added error: at_least_one_transaction.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All types of splits must be equal.
|
||||
*/
|
||||
public function validateTransactionTypes(Validator $validator): void
|
||||
{
|
||||
if ($validator->errors()->count() > 0) {
|
||||
return;
|
||||
}
|
||||
app('log')->debug('Now in validateTransactionTypes()');
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
|
||||
$types = [];
|
||||
foreach ($transactions as $transaction) {
|
||||
$types[] = $transaction['type'] ?? 'invalid';
|
||||
}
|
||||
$unique = array_unique($types);
|
||||
if (count($unique) > 1) {
|
||||
$validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal'));
|
||||
|
||||
return;
|
||||
}
|
||||
$first = $unique[0] ?? 'invalid';
|
||||
if ('invalid' === $first) {
|
||||
$validator->errors()->add('transactions.0.type', (string)trans('validation.invalid_transaction_type'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All types of splits must be equal.
|
||||
*/
|
||||
public function validateTransactionTypesForUpdate(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in validateTransactionTypesForUpdate()');
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
$types = [];
|
||||
foreach ($transactions as $transaction) {
|
||||
$originalType = $this->getOriginalType((int)($transaction['transaction_journal_id'] ?? 0));
|
||||
// if type is not set, fall back to the type of the journal, if one is given.
|
||||
$types[] = $transaction['type'] ?? $originalType;
|
||||
}
|
||||
$unique = array_unique($types);
|
||||
if (count($unique) > 1) {
|
||||
app('log')->warning('Add error for mismatch transaction types.');
|
||||
$validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal'));
|
||||
|
||||
return;
|
||||
}
|
||||
app('log')->debug('No errors in validateTransactionTypesForUpdate()');
|
||||
}
|
||||
|
||||
protected function getTransactionsArray(Validator $validator): array
|
||||
{
|
||||
app('log')->debug('Now in getTransactionsArray');
|
||||
@@ -95,15 +217,10 @@ trait TransactionValidation
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param string $transactionType
|
||||
* @param array $transaction
|
||||
*/
|
||||
protected function validateSingleAccount(Validator $validator, int $index, string $transactionType, array $transaction): void
|
||||
{
|
||||
app('log')->debug(sprintf('Now in validateSingleAccount(%d)', $index));
|
||||
|
||||
/** @var AccountValidator $accountValidator */
|
||||
$accountValidator = app(AccountValidator::class);
|
||||
|
||||
@@ -162,20 +279,13 @@ trait TransactionValidation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @param string $transactionType
|
||||
* @param int $index
|
||||
* @param array $source
|
||||
* @param array $destination
|
||||
*
|
||||
* @return void
|
||||
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
|
||||
*/
|
||||
protected function sanityCheckReconciliation(Validator $validator, string $transactionType, int $index, array $source, array $destination): void
|
||||
{
|
||||
app('log')->debug('Now in sanityCheckReconciliation');
|
||||
if (TransactionType::RECONCILIATION === ucfirst($transactionType) &&
|
||||
null === $source['id'] && null === $source['name'] && null === $destination['id'] && null === $destination['name']
|
||||
if (TransactionType::RECONCILIATION === ucfirst($transactionType)
|
||||
&& null === $source['id'] && null === $source['name'] && null === $destination['id'] && null === $destination['name']
|
||||
) {
|
||||
app('log')->debug('Both are NULL, error!');
|
||||
$validator->errors()->add(sprintf('transactions.%d.source_id', $index), trans('validation.reconciliation_either_account'));
|
||||
@@ -184,9 +294,9 @@ trait TransactionValidation
|
||||
$validator->errors()->add(sprintf('transactions.%d.destination_name', $index), trans('validation.reconciliation_either_account'));
|
||||
}
|
||||
|
||||
if (TransactionType::RECONCILIATION === $transactionType &&
|
||||
(null !== $source['id'] || null !== $source['name']) &&
|
||||
(null !== $destination['id'] || null !== $destination['name'])) {
|
||||
if (TransactionType::RECONCILIATION === $transactionType
|
||||
&& (null !== $source['id'] || null !== $source['name'])
|
||||
&& (null !== $destination['id'] || null !== $destination['name'])) {
|
||||
app('log')->debug('Both are not NULL, error!');
|
||||
$validator->errors()->add(sprintf('transactions.%d.source_id', $index), trans('validation.reconciliation_either_account'));
|
||||
$validator->errors()->add(sprintf('transactions.%d.source_name', $index), trans('validation.reconciliation_either_account'));
|
||||
@@ -195,208 +305,6 @@ trait TransactionValidation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO describe this method.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param AccountValidator $accountValidator
|
||||
* @param array $transaction
|
||||
* @param string $transactionType
|
||||
* @param int $index
|
||||
*
|
||||
* @return void
|
||||
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
|
||||
*/
|
||||
private function sanityCheckForeignCurrency(
|
||||
Validator $validator,
|
||||
AccountValidator $accountValidator,
|
||||
array $transaction,
|
||||
string $transactionType,
|
||||
int $index
|
||||
): void
|
||||
{
|
||||
app('log')->debug('Now in sanityCheckForeignCurrency()');
|
||||
if (0 !== $validator->errors()->count()) {
|
||||
app('log')->debug('Already have errors, return');
|
||||
return;
|
||||
}
|
||||
if (null === $accountValidator->source) {
|
||||
app('log')->debug('No source, return');
|
||||
return;
|
||||
}
|
||||
if (null === $accountValidator->destination) {
|
||||
app('log')->debug('No destination, return');
|
||||
return;
|
||||
}
|
||||
$source = $accountValidator->source;
|
||||
$destination = $accountValidator->destination;
|
||||
|
||||
app('log')->debug(sprintf('Source: #%d "%s (%s)"', $source->id, $source->name, $source->accountType->type));
|
||||
app('log')->debug(sprintf('Destination: #%d "%s" (%s)', $destination->id, $destination->name, $source->accountType->type));
|
||||
|
||||
if (!$this->isLiabilityOrAsset($source) || !$this->isLiabilityOrAsset($destination)) {
|
||||
app('log')->debug('Any account must be liability or asset account to continue.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$sourceCurrency = $accountRepository->getAccountCurrency($source) ?? $defaultCurrency;
|
||||
$destinationCurrency = $accountRepository->getAccountCurrency($destination) ?? $defaultCurrency;
|
||||
// if both accounts have the same currency, continue.
|
||||
if ($sourceCurrency->code === $destinationCurrency->code) {
|
||||
app('log')->debug('Both accounts have the same currency, continue.');
|
||||
return;
|
||||
}
|
||||
app('log')->debug(sprintf('Source account expects %s', $sourceCurrency->code));
|
||||
app('log')->debug(sprintf('Destination account expects %s', $destinationCurrency->code));
|
||||
|
||||
app('log')->debug(sprintf('Amount is %s', $transaction['amount']));
|
||||
|
||||
if (TransactionType::DEPOSIT === ucfirst($transactionType)) {
|
||||
app('log')->debug(sprintf('Processing as a "%s"', $transactionType));
|
||||
// use case: deposit from liability account to an asset account
|
||||
// the foreign amount must be in the currency of the source
|
||||
// the amount must be in the currency of the destination
|
||||
|
||||
// no foreign currency information is present:
|
||||
if (!$this->hasForeignCurrencyInfo($transaction)) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_currency'));
|
||||
return;
|
||||
}
|
||||
|
||||
// wrong currency information is present
|
||||
$foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false;
|
||||
$foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0);
|
||||
app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction);
|
||||
if ($foreignCurrencyCode !== $sourceCurrency->code && $foreignCurrencyId !== $sourceCurrency->id) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_currency_code', $index), (string)trans('validation.require_foreign_src'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (TransactionType::TRANSFER === ucfirst($transactionType) || TransactionType::WITHDRAWAL === ucfirst($transactionType)) {
|
||||
app('log')->debug(sprintf('Processing as a "%s"', $transactionType));
|
||||
// use case: withdrawal from asset account to a liability account.
|
||||
// the foreign amount must be in the currency of the destination
|
||||
// the amount must be in the currency of the source
|
||||
|
||||
// use case: transfer between accounts with different currencies.
|
||||
// the foreign amount must be in the currency of the destination
|
||||
// the amount must be in the currency of the source
|
||||
|
||||
// no foreign currency information is present:
|
||||
if (!$this->hasForeignCurrencyInfo($transaction)) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_currency'));
|
||||
return;
|
||||
}
|
||||
|
||||
// wrong currency information is present
|
||||
$foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false;
|
||||
$foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0);
|
||||
app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction);
|
||||
if ($foreignCurrencyCode !== $destinationCurrency->code && $foreignCurrencyId !== $destinationCurrency->id) {
|
||||
app('log')->debug(sprintf('No match on code, "%s" vs "%s"', $foreignCurrencyCode, $destinationCurrency->code));
|
||||
app('log')->debug(sprintf('No match on ID, #%d vs #%d', $foreignCurrencyId, $destinationCurrency->id));
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_dest'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isLiabilityOrAsset(Account $account): bool
|
||||
{
|
||||
return $this->isLiability($account) || $this->isAsset($account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isLiability(Account $account): bool
|
||||
{
|
||||
$type = $account->accountType->type;
|
||||
if (in_array($type, config('firefly.valid_liabilities'), true)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isAsset(Account $account): bool
|
||||
{
|
||||
$type = $account->accountType->type;
|
||||
return $type === AccountType::ASSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $transaction
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasForeignCurrencyInfo(array $transaction): bool
|
||||
{
|
||||
if (!array_key_exists('foreign_currency_code', $transaction) && !array_key_exists('foreign_currency_id', $transaction)) {
|
||||
return false;
|
||||
}
|
||||
if (!array_key_exists('foreign_amount', $transaction)) {
|
||||
return false;
|
||||
}
|
||||
if ('' === $transaction['foreign_amount']) {
|
||||
return false;
|
||||
}
|
||||
if (bccomp('0', $transaction['foreign_amount']) === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given account information. Switches on given transaction type.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function validateAccountInformationUpdate(Validator $validator, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
app('log')->debug('Now in validateAccountInformationUpdate()');
|
||||
if ($validator->errors()->count() > 0) {
|
||||
app('log')->debug('Validator already has errors, so return.');
|
||||
return;
|
||||
}
|
||||
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
|
||||
/**
|
||||
* @var int|null $index
|
||||
* @var array $transaction
|
||||
*/
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
if (!is_int($index)) {
|
||||
throw new FireflyException('Invalid data submitted: transaction is not array.');
|
||||
}
|
||||
$this->validateSingleUpdate($validator, $index, $transaction, $transactionGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @param int $index
|
||||
* @param array $transaction
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*/
|
||||
protected function validateSingleUpdate(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
app('log')->debug('Now validating single account update in validateSingleUpdate()');
|
||||
@@ -411,6 +319,7 @@ trait TransactionValidation
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// create validator:
|
||||
/** @var AccountValidator $accountValidator */
|
||||
$accountValidator = app(AccountValidator::class);
|
||||
@@ -420,10 +329,10 @@ trait TransactionValidation
|
||||
|
||||
// validate if the submitted source ID/name/iban/number are valid
|
||||
if (
|
||||
array_key_exists('source_id', $transaction) ||
|
||||
array_key_exists('source_name', $transaction) ||
|
||||
array_key_exists('source_iban', $transaction) ||
|
||||
array_key_exists('source_number', $transaction)
|
||||
array_key_exists('source_id', $transaction)
|
||||
|| array_key_exists('source_name', $transaction)
|
||||
|| array_key_exists('source_iban', $transaction)
|
||||
|| array_key_exists('source_number', $transaction)
|
||||
) {
|
||||
app('log')->debug('Will try to validate source account information.');
|
||||
$sourceId = (int)($transaction['source_id'] ?? 0);
|
||||
@@ -448,11 +357,10 @@ trait TransactionValidation
|
||||
}
|
||||
|
||||
if (
|
||||
array_key_exists('destination_id', $transaction) ||
|
||||
array_key_exists('destination_name', $transaction) ||
|
||||
array_key_exists('destination_iban', $transaction) ||
|
||||
array_key_exists('destination_number', $transaction)
|
||||
|
||||
array_key_exists('destination_id', $transaction)
|
||||
|| array_key_exists('destination_name', $transaction)
|
||||
|| array_key_exists('destination_iban', $transaction)
|
||||
|| array_key_exists('destination_number', $transaction)
|
||||
) {
|
||||
app('log')->debug('Will try to validate destination account information.');
|
||||
// at this point the validator may not have a source account, because it was never submitted for validation.
|
||||
@@ -484,22 +392,158 @@ trait TransactionValidation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionGroup $group
|
||||
* @param array $transactions
|
||||
* TODO describe this method.
|
||||
*
|
||||
* @return string
|
||||
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
|
||||
*/
|
||||
private function sanityCheckForeignCurrency(
|
||||
Validator $validator,
|
||||
AccountValidator $accountValidator,
|
||||
array $transaction,
|
||||
string $transactionType,
|
||||
int $index
|
||||
): void {
|
||||
app('log')->debug('Now in sanityCheckForeignCurrency()');
|
||||
if (0 !== $validator->errors()->count()) {
|
||||
app('log')->debug('Already have errors, return');
|
||||
|
||||
return;
|
||||
}
|
||||
if (null === $accountValidator->source) {
|
||||
app('log')->debug('No source, return');
|
||||
|
||||
return;
|
||||
}
|
||||
if (null === $accountValidator->destination) {
|
||||
app('log')->debug('No destination, return');
|
||||
|
||||
return;
|
||||
}
|
||||
$source = $accountValidator->source;
|
||||
$destination = $accountValidator->destination;
|
||||
|
||||
app('log')->debug(sprintf('Source: #%d "%s (%s)"', $source->id, $source->name, $source->accountType->type));
|
||||
app('log')->debug(sprintf('Destination: #%d "%s" (%s)', $destination->id, $destination->name, $source->accountType->type));
|
||||
|
||||
if (!$this->isLiabilityOrAsset($source) || !$this->isLiabilityOrAsset($destination)) {
|
||||
app('log')->debug('Any account must be liability or asset account to continue.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$sourceCurrency = $accountRepository->getAccountCurrency($source) ?? $defaultCurrency;
|
||||
$destinationCurrency = $accountRepository->getAccountCurrency($destination) ?? $defaultCurrency;
|
||||
// if both accounts have the same currency, continue.
|
||||
if ($sourceCurrency->code === $destinationCurrency->code) {
|
||||
app('log')->debug('Both accounts have the same currency, continue.');
|
||||
|
||||
return;
|
||||
}
|
||||
app('log')->debug(sprintf('Source account expects %s', $sourceCurrency->code));
|
||||
app('log')->debug(sprintf('Destination account expects %s', $destinationCurrency->code));
|
||||
|
||||
app('log')->debug(sprintf('Amount is %s', $transaction['amount']));
|
||||
|
||||
if (TransactionType::DEPOSIT === ucfirst($transactionType)) {
|
||||
app('log')->debug(sprintf('Processing as a "%s"', $transactionType));
|
||||
// use case: deposit from liability account to an asset account
|
||||
// the foreign amount must be in the currency of the source
|
||||
// the amount must be in the currency of the destination
|
||||
|
||||
// no foreign currency information is present:
|
||||
if (!$this->hasForeignCurrencyInfo($transaction)) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_currency'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// wrong currency information is present
|
||||
$foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false;
|
||||
$foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0);
|
||||
app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction);
|
||||
if ($foreignCurrencyCode !== $sourceCurrency->code && $foreignCurrencyId !== $sourceCurrency->id) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_currency_code', $index), (string)trans('validation.require_foreign_src'));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (TransactionType::TRANSFER === ucfirst($transactionType) || TransactionType::WITHDRAWAL === ucfirst($transactionType)) {
|
||||
app('log')->debug(sprintf('Processing as a "%s"', $transactionType));
|
||||
// use case: withdrawal from asset account to a liability account.
|
||||
// the foreign amount must be in the currency of the destination
|
||||
// the amount must be in the currency of the source
|
||||
|
||||
// use case: transfer between accounts with different currencies.
|
||||
// the foreign amount must be in the currency of the destination
|
||||
// the amount must be in the currency of the source
|
||||
|
||||
// no foreign currency information is present:
|
||||
if (!$this->hasForeignCurrencyInfo($transaction)) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_currency'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// wrong currency information is present
|
||||
$foreignCurrencyCode = $transaction['foreign_currency_code'] ?? false;
|
||||
$foreignCurrencyId = (int)($transaction['foreign_currency_id'] ?? 0);
|
||||
app('log')->debug(sprintf('Foreign currency code seems to be #%d "%s"', $foreignCurrencyId, $foreignCurrencyCode), $transaction);
|
||||
if ($foreignCurrencyCode !== $destinationCurrency->code && $foreignCurrencyId !== $destinationCurrency->id) {
|
||||
app('log')->debug(sprintf('No match on code, "%s" vs "%s"', $foreignCurrencyCode, $destinationCurrency->code));
|
||||
app('log')->debug(sprintf('No match on ID, #%d vs #%d', $foreignCurrencyId, $destinationCurrency->id));
|
||||
$validator->errors()->add(sprintf('transactions.%d.foreign_amount', $index), (string)trans('validation.require_foreign_dest'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isLiabilityOrAsset(Account $account): bool
|
||||
{
|
||||
return $this->isLiability($account) || $this->isAsset($account);
|
||||
}
|
||||
|
||||
private function isLiability(Account $account): bool
|
||||
{
|
||||
$type = $account->accountType->type;
|
||||
if (in_array($type, config('firefly.valid_liabilities'), true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isAsset(Account $account): bool
|
||||
{
|
||||
$type = $account->accountType->type;
|
||||
|
||||
return AccountType::ASSET === $type;
|
||||
}
|
||||
|
||||
private function hasForeignCurrencyInfo(array $transaction): bool
|
||||
{
|
||||
if (!array_key_exists('foreign_currency_code', $transaction) && !array_key_exists('foreign_currency_id', $transaction)) {
|
||||
return false;
|
||||
}
|
||||
if (!array_key_exists('foreign_amount', $transaction)) {
|
||||
return false;
|
||||
}
|
||||
if ('' === $transaction['foreign_amount']) {
|
||||
return false;
|
||||
}
|
||||
if (0 === bccomp('0', $transaction['foreign_amount'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getTransactionType(TransactionGroup $group, array $transactions): string
|
||||
{
|
||||
return $transactions[0]['type'] ?? strtolower((string)$group->transactionJournals()->first()?->transactionType->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $transaction
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
private function getOriginalSource(array $transaction, TransactionGroup $transactionGroup): ?Account
|
||||
{
|
||||
if (1 === $transactionGroup->transactionJournals->count()) {
|
||||
@@ -507,6 +551,7 @@ trait TransactionValidation
|
||||
|
||||
return $journal?->transactions()->where('amount', '<', 0)->first()?->account;
|
||||
}
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($transactionGroup->transactionJournals as $journal) {
|
||||
$journalId = (int)($transaction['transaction_journal_id'] ?? 0);
|
||||
@@ -518,129 +563,13 @@ trait TransactionValidation
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no transactions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateOneRecurrenceTransaction(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in validateOneRecurrenceTransaction()');
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
|
||||
// need at least one transaction
|
||||
if (0 === count($transactions)) {
|
||||
$validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error to the validator when there are no transactions in the array of data.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateOneTransaction(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in validateOneTransaction');
|
||||
if ($validator->errors()->count() > 0) {
|
||||
app('log')->debug('Validator already has errors, so return.');
|
||||
return;
|
||||
}
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
// need at least one transaction
|
||||
if (0 === count($transactions)) {
|
||||
$validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction'));
|
||||
app('log')->debug('Added error: at_least_one_transaction.');
|
||||
|
||||
return;
|
||||
}
|
||||
app('log')->debug('Added NO errors.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateTransactionArray(Validator $validator): void
|
||||
{
|
||||
if ($validator->errors()->count() > 0) {
|
||||
return;
|
||||
}
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
foreach (array_keys($transactions) as $key) {
|
||||
if (!is_int($key)) {
|
||||
$validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction'));
|
||||
app('log')->debug('Added error: at_least_one_transaction.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All types of splits must be equal.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateTransactionTypes(Validator $validator): void
|
||||
{
|
||||
if ($validator->errors()->count() > 0) {
|
||||
return;
|
||||
}
|
||||
app('log')->debug('Now in validateTransactionTypes()');
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
|
||||
$types = [];
|
||||
foreach ($transactions as $transaction) {
|
||||
$types[] = $transaction['type'] ?? 'invalid';
|
||||
}
|
||||
$unique = array_unique($types);
|
||||
if (count($unique) > 1) {
|
||||
$validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal'));
|
||||
|
||||
return;
|
||||
}
|
||||
$first = $unique[0] ?? 'invalid';
|
||||
if ('invalid' === $first) {
|
||||
$validator->errors()->add('transactions.0.type', (string)trans('validation.invalid_transaction_type'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All types of splits must be equal.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateTransactionTypesForUpdate(Validator $validator): void
|
||||
{
|
||||
app('log')->debug('Now in validateTransactionTypesForUpdate()');
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
$types = [];
|
||||
foreach ($transactions as $transaction) {
|
||||
$originalType = $this->getOriginalType((int)($transaction['transaction_journal_id'] ?? 0));
|
||||
// if type is not set, fall back to the type of the journal, if one is given.
|
||||
$types[] = $transaction['type'] ?? $originalType;
|
||||
}
|
||||
$unique = array_unique($types);
|
||||
if (count($unique) > 1) {
|
||||
app('log')->warning('Add error for mismatch transaction types.');
|
||||
$validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal'));
|
||||
|
||||
return;
|
||||
}
|
||||
app('log')->debug('No errors in validateTransactionTypesForUpdate()');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $journalId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getOriginalType(int $journalId): string
|
||||
{
|
||||
if (0 === $journalId) {
|
||||
return 'invalid';
|
||||
}
|
||||
/** @var TransactionJournal|null $journal */
|
||||
|
||||
/** @var null|TransactionJournal $journal */
|
||||
$journal = TransactionJournal::with(['transactionType'])->find($journalId);
|
||||
if (null !== $journal) {
|
||||
return strtolower($journal->transactionType->type);
|
||||
@@ -649,9 +578,6 @@ trait TransactionValidation
|
||||
return 'invalid';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
*/
|
||||
private function validateEqualAccounts(Validator $validator): void
|
||||
{
|
||||
if ($validator->errors()->count() > 0) {
|
||||
@@ -673,35 +599,38 @@ trait TransactionValidation
|
||||
}
|
||||
$sources = array_unique($sources);
|
||||
$dests = array_unique($dests);
|
||||
|
||||
switch ($type) {
|
||||
default:
|
||||
case 'withdrawal':
|
||||
if (count($sources) > 1) {
|
||||
$validator->errors()->add('transactions.0.source_id', (string)trans('validation.all_accounts_equal'));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'deposit':
|
||||
if (count($dests) > 1) {
|
||||
$validator->errors()->add('transactions.0.destination_id', (string)trans('validation.all_accounts_equal'));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case'transfer':
|
||||
if (count($sources) > 1 || count($dests) > 1) {
|
||||
$validator->errors()->add('transactions.0.source_id', (string)trans('validation.all_accounts_equal'));
|
||||
$validator->errors()->add('transactions.0.destination_id', (string)trans('validation.all_accounts_equal'));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*/
|
||||
private function validateEqualAccountsForUpdate(Validator $validator, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
if ($validator->errors()->count() > 0) {
|
||||
app('log')->debug('Validator already has errors, so return.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -740,17 +669,13 @@ trait TransactionValidation
|
||||
app('log')->debug('No errors found in validateEqualAccountsForUpdate');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $transactions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function collectComparisonData(array $transactions): array
|
||||
{
|
||||
$fields = ['source_id', 'destination_id', 'source_name', 'destination_name'];
|
||||
$comparison = [];
|
||||
foreach ($fields as $field) {
|
||||
$comparison[$field] = [];
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
// source or destination may be omitted. If this is the case, use the original source / destination name + ID.
|
||||
@@ -764,11 +689,6 @@ trait TransactionValidation
|
||||
return $comparison;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $journalId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getOriginalData(int $journalId): array
|
||||
{
|
||||
$return = [
|
||||
@@ -780,13 +700,15 @@ trait TransactionValidation
|
||||
if (0 === $journalId) {
|
||||
return $return;
|
||||
}
|
||||
/** @var Transaction|null $source */
|
||||
|
||||
/** @var null|Transaction $source */
|
||||
$source = Transaction::where('transaction_journal_id', $journalId)->where('amount', '<', 0)->with(['account'])->first();
|
||||
if (null !== $source) {
|
||||
$return['source_id'] = $source->account_id;
|
||||
$return['source_name'] = $source->account->name;
|
||||
}
|
||||
/** @var Transaction|null $destination */
|
||||
|
||||
/** @var null|Transaction $destination */
|
||||
$destination = Transaction::where('transaction_journal_id', $journalId)->where('amount', '>', 0)->with(['account'])->first();
|
||||
if (null !== $destination) {
|
||||
$return['destination_id'] = $destination->account_id;
|
||||
@@ -796,12 +718,6 @@ trait TransactionValidation
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array $comparison
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function compareAccountData(string $type, array $comparison): bool
|
||||
{
|
||||
return match ($type) {
|
||||
@@ -811,11 +727,6 @@ trait TransactionValidation
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $comparison
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function compareAccountDataWithdrawal(array $comparison): bool
|
||||
{
|
||||
if ($this->arrayEqual($comparison['source_id'])) {
|
||||
@@ -830,21 +741,11 @@ trait TransactionValidation
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function arrayEqual(array $array): bool
|
||||
{
|
||||
return 1 === count(array_unique($array));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $comparison
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function compareAccountDataDeposit(array $comparison): bool
|
||||
{
|
||||
if ($this->arrayEqual($comparison['destination_id'])) {
|
||||
@@ -859,11 +760,6 @@ trait TransactionValidation
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $comparison
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function compareAccountDataTransfer(array $comparison): bool
|
||||
{
|
||||
if ($this->arrayEqual($comparison['source_id'])) {
|
||||
|
||||
Reference in New Issue
Block a user