Add feature: Firefly III can create account of the right type if you submit ID of the wrong type.

This commit is contained in:
James Cole 2023-01-14 07:39:51 +01:00
parent 3033b0fa7f
commit 4ccdd8f322
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
3 changed files with 99 additions and 56 deletions

View File

@ -69,28 +69,38 @@ trait JournalServiceTrait
unset($array); unset($array);
// and now try to find it, based on the type of transaction. // and now try to find it, based on the type of transaction.
$message = 'Based on the fact that the transaction is a %s, the %s account should be in: %s. Direction is %s.'; $message = 'Transaction = %s, %s account should be in: %s. Direction is %s.';
Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType] ?? ['UNKNOWN']), $direction)); Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType] ?? ['UNKNOWN']), $direction));
Log::debug('Now searching by ID'); $result = $this->findAccountById($data, $expectedTypes[$transactionType]);
$result = $this->findAccountById($data, $expectedTypes[$transactionType]); $result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]);
Log::debug('If nothing is found, searching by IBAN'); $ibanResult = $result;
$result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]);
$ibanResult = $result;
// if result is NULL but IBAN is set, any result of the search by NAME can't overrule
// this account. In such a case, the name search must be retried with a new name.
Log::debug('If nothing is found, searching by number');
$result = $this->findAccountByNumber($result, $data, $expectedTypes[$transactionType]); $result = $this->findAccountByNumber($result, $data, $expectedTypes[$transactionType]);
$numberResult = $result; $numberResult = $result;
Log::debug('If nothing is found, searching by name'); $result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
$result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
// if result is NULL but IBAN is set, any result of the search by NAME can't overrule
// this account. In such a case, the name search must be retried with a new name.
if (null !== $result && null === $numberResult && null === $ibanResult && null !== $data['iban']) { if (null !== $result && null === $numberResult && null === $ibanResult && null !== $data['iban']) {
$data['name'] = sprintf('%s (%s)', $data['name'], $data['iban']); $data['name'] = sprintf('%s (%s)', $data['name'], $data['iban']);
Log::debug(sprintf('Search again using the new name, "%s".', $data['name'])); Log::debug(sprintf('Search again using the new name, "%s".', $data['name']));
$result = $this->findAccountByName(null, $data, $expectedTypes[$transactionType]); $result = $this->findAccountByName(null, $data, $expectedTypes[$transactionType]);
} }
// if the result is NULL but the ID is set, an account could exist of the wrong type.
// that data can be used to create a new account of the right type.
if (null === $result && null !== $data['id']) {
Log::debug(sprintf('Account #%d may exist and be of the wrong type, use data to create one of the right type.', $data['id']));
$temp = $this->findAccountById(['id' => $data['id']], []);
if (null !== $temp) {
$tempData = [
'name' => $temp->name,
'iban' => $temp->iban,
'number' => null,
'bic' => null,
];
$result = $this->createAccount(null, $tempData, $expectedTypes[$transactionType][0]);
}
}
Log::debug('If nothing is found, create it.'); Log::debug('If nothing is found, create it.');
$result = $this->createAccount($result, $data, $expectedTypes[$transactionType][0]); $result = $this->createAccount($result, $data, $expectedTypes[$transactionType][0]);
@ -106,18 +116,24 @@ trait JournalServiceTrait
*/ */
private function findAccountById(array $data, array $types): ?Account private function findAccountById(array $data, array $types): ?Account
{ {
$search = null;
// first attempt, find by ID. // first attempt, find by ID.
if (null !== $data['id']) { if (null !== $data['id']) {
$search = $this->accountRepository->find((int)$data['id']); $search = $this->accountRepository->find((int)$data['id']);
if (null !== $search && in_array($search->accountType->type, $types, true)) { if (null !== $search && in_array($search->accountType->type, $types, true)) {
Log::debug( Log::debug(
sprintf('Found "account_id" object: #%d, "%s" of type %s', $search->id, $search->name, $search->accountType->type) sprintf('Found "account_id" object: #%d, "%s" of type %s (1)', $search->id, $search->name, $search->accountType->type)
); );
return $search;
}
if (null !== $search && 0 === count($types)) {
Log::debug(
sprintf('Found "account_id" object: #%d, "%s" of type %s (2)', $search->id, $search->name, $search->accountType->type)
);
return $search;
} }
} }
Log::debug(sprintf('Found no account by ID #%d of types', $data['id']), $types);
return $search; return null;
} }
/** /**
@ -129,22 +145,26 @@ trait JournalServiceTrait
*/ */
private function findAccountByIban(?Account $account, array $data, array $types): ?Account private function findAccountByIban(?Account $account, array $data, array $types): ?Account
{ {
// third attempt, find by IBAN if (null !== $account) {
if (null === $account && null !== $data['iban']) { Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name));
Log::debug(sprintf('Found nothing by account iban "%s".', $data['iban'])); return $account;
// find by preferred type.
$source = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByIbanNull($data['iban'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_iban" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
} }
if (null === $data['iban'] || '' === $data['iban']) {
Log::debug('IBAN is empty, will not search for IBAN.');
return null;
}
// find by preferred type.
$source = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByIbanNull($data['iban'], $types);
return $account; if (null !== $source) {
Log::debug(sprintf('Found "account_iban" object: #%d, %s', $source->id, $source->name));
return $source;
}
Log::debug(sprintf('Found no account with IBAN "%s" of expected types', $data['iban']), $types);
return null;
} }
/** /**
@ -156,23 +176,28 @@ trait JournalServiceTrait
*/ */
private function findAccountByNumber(?Account $account, array $data, array $types): ?Account private function findAccountByNumber(?Account $account, array $data, array $types): ?Account
{ {
// third attempt, find by account number if (null !== $account) {
if (null === $account && null !== $data['number'] && '' !== (string)$data['number']) { Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name));
Log::debug(sprintf('Searching for account number "%s".', $data['number'])); return $account;
// find by preferred type. }
$source = $this->accountRepository->findByAccountNumber((string)$data['number'], [$types[0]]); if (null === $data['number'] || '' === $data['number']) {
Log::debug('Account number is empty, will not search for account number.');
return null;
}
// find by preferred type.
$source = $this->accountRepository->findByAccountNumber((string)$data['number'], [$types[0]]);
// or any expected type. // or any expected type.
$source = $source ?? $this->accountRepository->findByAccountNumber((string)$data['number'], $types); $source = $source ?? $this->accountRepository->findByAccountNumber((string)$data['number'], $types);
if (null !== $source) { if (null !== $source) {
Log::debug(sprintf('Found account: #%d, %s', $source->id, $source->name)); Log::debug(sprintf('Found account: #%d, %s', $source->id, $source->name));
$account = $source; return $source;
}
} }
return $account; Log::debug(sprintf('Found no account with account number "%s" of expected types', $data['number']), $types);
return null;
} }
/** /**
@ -184,21 +209,28 @@ trait JournalServiceTrait
*/ */
private function findAccountByName(?Account $account, array $data, array $types): ?Account private function findAccountByName(?Account $account, array $data, array $types): ?Account
{ {
// second attempt, find by name. if (null !== $account) {
if (null === $account && null !== $data['name']) { Log::debug(sprintf('Already have account #%d ("%s"), return that.', $account->id, $account->name));
// find by preferred type. return $account;
$source = $this->accountRepository->findByName($data['name'], [$types[0]]); }
// or any expected type. if (null === $data['name'] || '' === $data['name']) {
$source = $source ?? $this->accountRepository->findByName($data['name'], $types); Log::debug('Account name is empty, will not search for account name.');
return null;
if (null !== $source) {
Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
} }
return $account; // find by preferred type.
$source = $this->accountRepository->findByName($data['name'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByName($data['name'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name));
return $source;
}
Log::debug(sprintf('Found no account with account name "%s" of expected types', $data['name']), $types);
return null;
} }
/** /**

View File

@ -106,7 +106,11 @@ trait DepositValidation
$accountIban = array_key_exists('iban', $array) ? $array['iban'] : null; $accountIban = array_key_exists('iban', $array) ? $array['iban'] : null;
$accountNumber = array_key_exists('number', $array) ? $array['number'] : null; $accountNumber = array_key_exists('number', $array) ? $array['number'] : null;
Log::debug('Now in validateDepositSource', $array); Log::debug('Now in validateDepositSource', $array);
// null = we found nothing at all or didnt even search
// false = invalid results
$result = null; $result = null;
// source can be any of the following types. // source can be any of the following types.
$validTypes = array_keys($this->combinations[$this->transactionType]); $validTypes = array_keys($this->combinations[$this->transactionType]);
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) { if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
@ -123,7 +127,7 @@ trait DepositValidation
$search = $this->accountRepository->find($accountId); $search = $this->accountRepository->find($accountId);
if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) { if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) {
Log::debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type)); Log::debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type));
$result = false; Log::debug(sprintf('Firefly III accepts ID #%d as valid account data.', $accountId));
} }
} }

View File

@ -2,6 +2,13 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
Alpha 2
- Data import: when you submit a transaction but give the ID of an account of the wrong type, Firefly III will try to create an account of the right type. For example: you submit a deposit but the source account is an expense account: Firefly III will try to create a revenue account instead.
## 5.8.0-alpha.1 - 2023-01-08 ## 5.8.0-alpha.1 - 2023-01-08
This is the first release of the new 5.8.0 series of Firefly III. It should upgrade the database automatically BUT This is the first release of the new 5.8.0 series of Firefly III. It should upgrade the database automatically BUT