Code cleanup.

This commit is contained in:
James Cole
2023-12-20 19:35:52 +01:00
parent c4f6366642
commit 64ec0cf62e
997 changed files with 12908 additions and 28136 deletions

View File

@@ -29,6 +29,4 @@ namespace FireflyIII\Validation\Account;
*
* Trait AccountValidatorProperties
*/
trait AccountValidatorProperties
{
}
trait AccountValidatorProperties {}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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'));

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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
{

View File

@@ -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.');

View File

@@ -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'])) {