Clean up old tests.

This commit is contained in:
James Cole
2021-03-12 18:31:19 +01:00
parent a05d006fa7
commit 81f5224b11
36 changed files with 792 additions and 3261 deletions

View File

@@ -108,31 +108,31 @@ class AccountUpdateService
/**
* @param Account $account
* @param array $data
*
* @return Account
*/
private function updateLocation(Account $account, array $data): void
private function updateAccount(Account $account, array $data): Account
{
$updateLocation = $data['update_location'] ?? false;
// location must be updated?
if (true === $updateLocation) {
// if all set to NULL, delete
if (null === $data['latitude'] && null === $data['longitude'] && null === $data['zoom_level']) {
$account->locations()->delete();
}
// update the account itself:
$account->name = $data['name'] ?? $account->name;
$account->active = $data['active'] ?? $account->active;
$account->iban = $data['iban'] ?? $account->iban;
// otherwise, update or create.
if (!(null === $data['latitude'] && null === $data['longitude'] && null === $data['zoom_level'])) {
$location = $this->accountRepository->getLocation($account);
if (null === $location) {
$location = new Location;
$location->locatable()->associate($account);
}
$location->latitude = $data['latitude'] ?? config('firefly.default_location.latitude');
$location->longitude = $data['longitude'] ?? config('firefly.default_location.longitude');
$location->zoom_level = $data['zoom_level'] ?? config('firefly.default_location.zoom_level');
$location->save();
}
// liability stuff:
$liabilityType = $data['liability_type'] ?? '';
if ($this->isLiability($account) && $this->isLiabilityType($liabilityType)) {
$type = $this->getAccountType($liabilityType);
$account->account_type_id = $type->id;
}
// update virtual balance (could be set to zero if empty string).
if (null !== $data['virtual_balance']) {
$account->virtual_balance = '' === trim($data['virtual_balance']) ? '0' : $data['virtual_balance'];
}
$account->save();
return $account;
}
/**
@@ -169,93 +169,6 @@ class AccountUpdateService
return AccountType::whereType($type)->first();
}
/**
* @param Account $account
* @param array $data
*
* @return Account
*/
private function updateAccount(Account $account, array $data): Account
{
// update the account itself:
$account->name = $data['name'] ?? $account->name;
$account->active = $data['active'] ?? $account->active;
$account->iban = $data['iban'] ?? $account->iban;
// liability stuff:
$liabilityType = $data['liability_type'] ?? '';
if ($this->isLiability($account) && $this->isLiabilityType($liabilityType)) {
$type = $this->getAccountType($liabilityType);
$account->account_type_id = $type->id;
}
// update virtual balance (could be set to zero if empty string).
if (null !== $data['virtual_balance']) {
$account->virtual_balance = '' === trim($data['virtual_balance']) ? '0' : $data['virtual_balance'];
}
$account->save();
return $account;
}
/**
* @param Account $account
* @param array $data
*/
private function updatePreferences(Account $account, array $data): void
{
Log::debug(sprintf('Now in updatePreferences(#%d)', $account->id));
if (array_key_exists('active', $data) && (false === $data['active'] || 0 === $data['active'])) {
Log::debug('Account was marked as inactive.');
$preference = app('preferences')->getForUser($account->user, 'frontpageAccounts');
if (null !== $preference) {
$removeAccountId = (int)$account->id;
$array = $preference->data;
Log::debug('Current list of accounts: ', $array);
Log::debug(sprintf('Going to remove account #%d', $removeAccountId));
$filtered = array_filter(
$array, function ($accountId) use ($removeAccountId) {
return (int)$accountId !== $removeAccountId;
}
);
Log::debug('Left with accounts', array_values($filtered));
app('preferences')->setForUser($account->user, 'frontpageAccounts', array_values($filtered));
app('preferences')->forget($account->user, 'frontpageAccounts');
return;
}
Log::debug("Found no frontpageAccounts preference, do nothing.");
return;
}
Log::debug('Account was not marked as inactive, do nothing.');
}
/**
* @param Account $account
* @param array $data
*/
private function updateOpeningBalance(Account $account, array $data): void
{
// has valid initial balance (IB) data?
$type = $account->accountType;
// if it can have a virtual balance, it can also have an opening balance.
if (in_array($type->type, $this->canHaveVirtual, true)) {
// check if is submitted as empty, that makes it valid:
if ($this->validOBData($data) && !$this->isEmptyOBData($data)) {
$this->updateOBGroup($account, $data);
}
if (!$this->validOBData($data) && $this->isEmptyOBData($data)) {
$this->deleteOBGroup($account);
}
}
}
/**
* @param Account $account
* @param array $data
@@ -314,4 +227,91 @@ class AccountUpdateService
return $return;
}
/**
* @param Account $account
* @param array $data
*/
private function updateLocation(Account $account, array $data): void
{
$updateLocation = $data['update_location'] ?? false;
// location must be updated?
if (true === $updateLocation) {
// if all set to NULL, delete
if (null === $data['latitude'] && null === $data['longitude'] && null === $data['zoom_level']) {
$account->locations()->delete();
}
// otherwise, update or create.
if (!(null === $data['latitude'] && null === $data['longitude'] && null === $data['zoom_level'])) {
$location = $this->accountRepository->getLocation($account);
if (null === $location) {
$location = new Location;
$location->locatable()->associate($account);
}
$location->latitude = $data['latitude'] ?? config('firefly.default_location.latitude');
$location->longitude = $data['longitude'] ?? config('firefly.default_location.longitude');
$location->zoom_level = $data['zoom_level'] ?? config('firefly.default_location.zoom_level');
$location->save();
}
}
}
/**
* @param Account $account
* @param array $data
*/
private function updateOpeningBalance(Account $account, array $data): void
{
// has valid initial balance (IB) data?
$type = $account->accountType;
// if it can have a virtual balance, it can also have an opening balance.
if (in_array($type->type, $this->canHaveVirtual, true)) {
// check if is submitted as empty, that makes it valid:
if ($this->validOBData($data) && !$this->isEmptyOBData($data)) {
$this->updateOBGroup($account, $data);
}
if (!$this->validOBData($data) && $this->isEmptyOBData($data)) {
$this->deleteOBGroup($account);
}
}
}
/**
* @param Account $account
* @param array $data
*/
private function updatePreferences(Account $account, array $data): void
{
Log::debug(sprintf('Now in updatePreferences(#%d)', $account->id));
if (array_key_exists('active', $data) && (false === $data['active'] || 0 === $data['active'])) {
Log::debug('Account was marked as inactive.');
$preference = app('preferences')->getForUser($account->user, 'frontpageAccounts');
if (null !== $preference) {
$removeAccountId = (int)$account->id;
$array = $preference->data;
Log::debug('Current list of accounts: ', $array);
Log::debug(sprintf('Going to remove account #%d', $removeAccountId));
$filtered = array_filter(
$array, function ($accountId) use ($removeAccountId) {
return (int)$accountId !== $removeAccountId;
}
);
Log::debug('Left with accounts', array_values($filtered));
app('preferences')->setForUser($account->user, 'frontpageAccounts', array_values($filtered));
app('preferences')->forget($account->user, 'frontpageAccounts');
return;
}
Log::debug("Found no frontpageAccounts preference, do nothing.");
return;
}
Log::debug('Account was not marked as inactive, do nothing.');
}
}

View File

@@ -136,6 +136,69 @@ class BillUpdateService
return $bill;
}
/**
* @param Bill $bill
* @param array $data
*
* @return Bill
*/
private function updateBillProperties(Bill $bill, array $data): Bill
{
if (isset($data['name']) && '' !== (string)$data['name']) {
$bill->name = $data['name'];
}
if (isset($data['amount_min']) && '' !== (string)$data['amount_min']) {
$bill->amount_min = $data['amount_min'];
}
if (isset($data['amount_max']) && '' !== (string)$data['amount_max']) {
$bill->amount_max = $data['amount_max'];
}
if (isset($data['date']) && '' !== (string)$data['date']) {
$bill->date = $data['date'];
}
if (isset($data['repeat_freq']) && '' !== (string)$data['repeat_freq']) {
$bill->repeat_freq = $data['repeat_freq'];
}
if (isset($data['skip']) && '' !== (string)$data['skip']) {
$bill->skip = $data['skip'];
}
if (isset($data['active']) && is_bool($data['active'])) {
$bill->active = $data['active'];
}
$bill->match = 'EMPTY';
$bill->automatch = true;
$bill->save();
return $bill;
}
/**
* @param Bill $bill
* @param int $oldOrder
* @param int $newOrder
*/
private function updateOrder(Bill $bill, int $oldOrder, int $newOrder): void
{
if ($newOrder > $oldOrder) {
$this->user->bills()->where('order', '<=', $newOrder)->where('order', '>', $oldOrder)
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order-1')]);
$bill->order = $newOrder;
$bill->save();
}
if ($newOrder < $oldOrder) {
$this->user->bills()->where('order', '>=', $newOrder)->where('order', '<', $oldOrder)
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order+1')]);
$bill->order = $newOrder;
$bill->save();
}
}
/**
* @param Bill $bill
* @param array $oldData
@@ -195,7 +258,6 @@ class BillUpdateService
}
}
/**
* @param Rule $rule
* @param string $key
@@ -206,67 +268,4 @@ class BillUpdateService
{
return $rule->ruleTriggers()->where('trigger_type', $key)->first();
}
/**
* @param Bill $bill
* @param int $oldOrder
* @param int $newOrder
*/
private function updateOrder(Bill $bill, int $oldOrder, int $newOrder): void
{
if ($newOrder > $oldOrder) {
$this->user->bills()->where('order', '<=', $newOrder)->where('order', '>', $oldOrder)
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order-1')]);
$bill->order = $newOrder;
$bill->save();
}
if ($newOrder < $oldOrder) {
$this->user->bills()->where('order', '>=', $newOrder)->where('order', '<', $oldOrder)
->where('bills.id', '!=', $bill->id)
->update(['order' => DB::raw('bills.order+1')]);
$bill->order = $newOrder;
$bill->save();
}
}
/**
* @param Bill $bill
* @param array $data
*
* @return Bill
*/
private function updateBillProperties(Bill $bill, array $data): Bill
{
if (isset($data['name']) && '' !== (string)$data['name']) {
$bill->name = $data['name'];
}
if (isset($data['amount_min']) && '' !== (string)$data['amount_min']) {
$bill->amount_min = $data['amount_min'];
}
if (isset($data['amount_max']) && '' !== (string)$data['amount_max']) {
$bill->amount_max = $data['amount_max'];
}
if (isset($data['date']) && '' !== (string)$data['date']) {
$bill->date = $data['date'];
}
if (isset($data['repeat_freq']) && '' !== (string)$data['repeat_freq']) {
$bill->repeat_freq = $data['repeat_freq'];
}
if (isset($data['skip']) && '' !== (string)$data['skip']) {
$bill->skip = $data['skip'];
}
if (isset($data['active']) && is_bool($data['active'])) {
$bill->active = $data['active'];
}
$bill->match = 'EMPTY';
$bill->automatch = true;
$bill->save();
return $bill;
}
}

View File

@@ -23,13 +23,13 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Update;
use Exception;
use FireflyIII\Models\Category;
use FireflyIII\Models\Note;
use FireflyIII\Models\RecurrenceTransactionMeta;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
use Log;
use Exception;
/**
* Class CategoryUpdateService
@@ -54,6 +54,14 @@ class CategoryUpdateService
}
}
/**
* @param mixed $user
*/
public function setUser($user): void
{
$this->user = $user;
}
/**
* @param Category $category
* @param array $data
@@ -75,27 +83,6 @@ class CategoryUpdateService
return $category;
}
/**
* @param string $oldName
* @param string $newName
*/
private function updateRuleActions(string $oldName, string $newName): void
{
$types = ['set_category',];
$actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_actions.action_type', $types)
->where('rule_actions.action_value', $oldName)
->get(['rule_actions.*']);
Log::debug(sprintf('Found %d actions to update.', $actions->count()));
/** @var RuleAction $action */
foreach ($actions as $action) {
$action->action_value = $newName;
$action->save();
Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
}
}
/**
* @param string $oldName
* @param string $newName
@@ -118,11 +105,24 @@ class CategoryUpdateService
}
/**
* @param mixed $user
* @param string $oldName
* @param string $newName
*/
public function setUser($user): void
private function updateRuleActions(string $oldName, string $newName): void
{
$this->user = $user;
$types = ['set_category',];
$actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_actions.action_type', $types)
->where('rule_actions.action_value', $oldName)
->get(['rule_actions.*']);
Log::debug(sprintf('Found %d actions to update.', $actions->count()));
/** @var RuleAction $action */
foreach ($actions as $action) {
$action->action_value = $newName;
$action->save();
Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
}
}
/**
@@ -145,7 +145,7 @@ class CategoryUpdateService
* @param Category $category
* @param array $data
*
* @throws \Exception
* @throws Exception
*/
private function updateNotes(Category $category, array $data): void
{

View File

@@ -108,16 +108,15 @@ class GroupCloneService
}
/**
* @param TransactionJournalMeta $meta
* @param TransactionJournal $newJournal
* @param Transaction $transaction
* @param TransactionJournal $newJournal
*/
private function cloneMeta(TransactionJournalMeta $meta, TransactionJournal $newJournal): void
private function cloneTransaction(Transaction $transaction, TransactionJournal $newJournal): void
{
$newMeta = $meta->replicate();
$newMeta->transaction_journal_id = $newJournal->id;
if ('recurrence_id' !== $newMeta->name) {
$newMeta->save();
}
$newTransaction = $transaction->replicate();
$newTransaction->transaction_journal_id = $newJournal->id;
$newTransaction->reconciled = false;
$newTransaction->save();
}
/**
@@ -137,15 +136,16 @@ class GroupCloneService
}
/**
* @param Transaction $transaction
* @param TransactionJournal $newJournal
* @param TransactionJournalMeta $meta
* @param TransactionJournal $newJournal
*/
private function cloneTransaction(Transaction $transaction, TransactionJournal $newJournal): void
private function cloneMeta(TransactionJournalMeta $meta, TransactionJournal $newJournal): void
{
$newTransaction = $transaction->replicate();
$newTransaction->transaction_journal_id = $newJournal->id;
$newTransaction->reconciled = false;
$newTransaction->save();
$newMeta = $meta->replicate();
$newMeta->transaction_journal_id = $newJournal->id;
if ('recurrence_id' !== $newMeta->name) {
$newMeta->save();
}
}

View File

@@ -96,37 +96,6 @@ class GroupUpdateService
return $transactionGroup;
}
/**
* @param TransactionGroup $transactionGroup
* @param array $data
*
* @throws FireflyException
*/
private function createTransactionJournal(TransactionGroup $transactionGroup, array $data): void
{
$submission = [
'transactions' => [
$data,
],
];
/** @var TransactionJournalFactory $factory */
$factory = app(TransactionJournalFactory::class);
$factory->setUser($transactionGroup->user);
try {
$collection = $factory->create($submission);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException(sprintf('Could not create new transaction journal: %s', $e->getMessage()));
}
$collection->each(
function (TransactionJournal $journal) use ($transactionGroup) {
$transactionGroup->transactionJournals()->save($journal);
}
);
}
/**
* Update single journal.
*
@@ -191,4 +160,35 @@ class GroupUpdateService
return $updated;
}
/**
* @param TransactionGroup $transactionGroup
* @param array $data
*
* @throws FireflyException
*/
private function createTransactionJournal(TransactionGroup $transactionGroup, array $data): void
{
$submission = [
'transactions' => [
$data,
],
];
/** @var TransactionJournalFactory $factory */
$factory = app(TransactionJournalFactory::class);
$factory->setUser($transactionGroup->user);
try {
$collection = $factory->create($submission);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException(sprintf('Could not create new transaction journal: %s', $e->getMessage()));
}
$collection->each(
function (TransactionJournal $journal) use ($transactionGroup) {
$transactionGroup->transactionJournals()->save($journal);
}
);
}
}

View File

@@ -161,47 +161,61 @@ class JournalUpdateService
}
/**
* Get destination transaction.
*
* @return Transaction
* @return bool
*/
private function getDestinationTransaction(): Transaction
private function hasValidAccounts(): bool
{
if (null === $this->destinationTransaction) {
$this->destinationTransaction = $this->transactionJournal->transactions()->where('amount', '>', 0)->first();
}
return $this->destinationTransaction;
return $this->hasValidSourceAccount() && $this->hasValidDestinationAccount();
}
/**
* This method returns the current or expected type of the journal (in case of a change) based on the data in the array.
*
* If the array contains key 'type' and the value is correct, this is returned. Otherwise, the original type is returned.
*
* @return string
* @return bool
*/
private function getExpectedType(): string
private function hasValidSourceAccount(): bool
{
Log::debug('Now in getExpectedType()');
if ($this->hasFields(['type'])) {
return ucfirst('opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']);
Log::debug('Now in hasValidSourceAccount().');
$sourceId = $this->data['source_id'] ?? null;
$sourceName = $this->data['source_name'] ?? null;
if (!$this->hasFields(['source_id', 'source_name'])) {
$origSourceAccount = $this->getOriginalSourceAccount();
$sourceId = $origSourceAccount->id;
$sourceName = $origSourceAccount->name;
}
return $this->transactionJournal->transactionType->type;
// make new account validator.
$expectedType = $this->getExpectedType();
Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType));
// make a new validator.
/** @var AccountValidator $validator */
$validator = app(AccountValidator::class);
$validator->setTransactionType($expectedType);
$validator->setUser($this->transactionJournal->user);
$result = $validator->validateSource($sourceId, $sourceName, null);
Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true)));
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
// validate submitted info:
return $result;
}
/**
* @return Account
* @param array $fields
*
* @return bool
*/
private function getOriginalDestinationAccount(): Account
private function hasFields(array $fields): bool
{
if (null === $this->destinationAccount) {
$destination = $this->getDestinationTransaction();
$this->destinationAccount = $destination->account;
foreach ($fields as $field) {
if (array_key_exists($field, $this->data)) {
return true;
}
}
return $this->destinationAccount;
return false;
}
/**
@@ -231,96 +245,20 @@ class JournalUpdateService
}
/**
* Does a validation and returns the destination account. This method will break if the dest isn't really valid.
* This method returns the current or expected type of the journal (in case of a change) based on the data in the array.
*
* @return Account
*/
private function getValidDestinationAccount(): Account
{
Log::debug('Now in getValidDestinationAccount().');
if (!$this->hasFields(['destination_id', 'destination_name'])) {
return $this->getOriginalDestinationAccount();
}
$destInfo = [
'id' => (int)($this->data['destination_id'] ?? null),
'name' => $this->data['destination_name'] ?? null,
'iban' => $this->data['destination_iban'] ?? null,
'number' => $this->data['destination_number'] ?? null,
'bic' => $this->data['destination_bic'] ?? null,
];
// make new account validator.
$expectedType = $this->getExpectedType();
Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType));
try {
$result = $this->getAccount($expectedType, 'destination', $destInfo);
} catch (FireflyException $e) {
Log::error(sprintf('getValidDestinationAccount() threw unexpected error: %s', $e->getMessage()));
$result = $this->getOriginalDestinationAccount();
}
return $result;
}
/**
* Does a validation and returns the source account. This method will break if the source isn't really valid.
* If the array contains key 'type' and the value is correct, this is returned. Otherwise, the original type is returned.
*
* @return Account
* @return string
*/
private function getValidSourceAccount(): Account
private function getExpectedType(): string
{
Log::debug('Now in getValidSourceAccount().');
if (!$this->hasFields(['source_id', 'source_name'])) {
return $this->getOriginalSourceAccount();
Log::debug('Now in getExpectedType()');
if ($this->hasFields(['type'])) {
return ucfirst('opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']);
}
$sourceInfo = [
'id' => (int)($this->data['source_id'] ?? null),
'name' => $this->data['source_name'] ?? null,
'iban' => $this->data['source_iban'] ?? null,
'number' => $this->data['source_number'] ?? null,
'bic' => $this->data['source_bic'] ?? null,
];
$expectedType = $this->getExpectedType();
try {
$result = $this->getAccount($expectedType, 'source', $sourceInfo);
} catch (FireflyException $e) {
Log::error(sprintf('Cant get the valid source account: %s', $e->getMessage()));
$result = $this->getOriginalSourceAccount();
}
Log::debug(sprintf('getValidSourceAccount() will return #%d ("%s")', $result->id, $result->name));
return $result;
}
/**
* @param array $fields
*
* @return bool
*/
private function hasFields(array $fields): bool
{
foreach ($fields as $field) {
if (array_key_exists($field, $this->data)) {
return true;
}
}
return false;
}
/**
* @return bool
*/
private function hasValidAccounts(): bool
{
return $this->hasValidSourceAccount() && $this->hasValidDestinationAccount();
return $this->transactionJournal->transactionType->type;
}
/**
@@ -361,36 +299,64 @@ class JournalUpdateService
}
/**
* @return bool
* @return Account
*/
private function hasValidSourceAccount(): bool
private function getOriginalDestinationAccount(): Account
{
Log::debug('Now in hasValidSourceAccount().');
$sourceId = $this->data['source_id'] ?? null;
$sourceName = $this->data['source_name'] ?? null;
if (!$this->hasFields(['source_id', 'source_name'])) {
$origSourceAccount = $this->getOriginalSourceAccount();
$sourceId = $origSourceAccount->id;
$sourceName = $origSourceAccount->name;
if (null === $this->destinationAccount) {
$destination = $this->getDestinationTransaction();
$this->destinationAccount = $destination->account;
}
// make new account validator.
return $this->destinationAccount;
}
/**
* Get destination transaction.
*
* @return Transaction
*/
private function getDestinationTransaction(): Transaction
{
if (null === $this->destinationTransaction) {
$this->destinationTransaction = $this->transactionJournal->transactions()->where('amount', '>', 0)->first();
}
return $this->destinationTransaction;
}
/**
* Does a validation and returns the source account. This method will break if the source isn't really valid.
*
* @return Account
*/
private function getValidSourceAccount(): Account
{
Log::debug('Now in getValidSourceAccount().');
if (!$this->hasFields(['source_id', 'source_name'])) {
return $this->getOriginalSourceAccount();
}
$sourceInfo = [
'id' => (int)($this->data['source_id'] ?? null),
'name' => $this->data['source_name'] ?? null,
'iban' => $this->data['source_iban'] ?? null,
'number' => $this->data['source_number'] ?? null,
'bic' => $this->data['source_bic'] ?? null,
];
$expectedType = $this->getExpectedType();
Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType));
try {
$result = $this->getAccount($expectedType, 'source', $sourceInfo);
} catch (FireflyException $e) {
Log::error(sprintf('Cant get the valid source account: %s', $e->getMessage()));
// make a new validator.
/** @var AccountValidator $validator */
$validator = app(AccountValidator::class);
$validator->setTransactionType($expectedType);
$validator->setUser($this->transactionJournal->user);
$result = $this->getOriginalSourceAccount();
}
$result = $validator->validateSource($sourceId, $sourceName, null);
Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true)));
Log::debug(sprintf('getValidSourceAccount() will return #%d ("%s")', $result->id, $result->name));
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
// validate submitted info:
return $result;
}
@@ -427,36 +393,68 @@ class JournalUpdateService
}
/**
* Does a validation and returns the destination account. This method will break if the dest isn't really valid.
*
* @return Account
*/
private function updateAmount(): void
private function getValidDestinationAccount(): Account
{
if (!$this->hasFields(['amount'])) {
return;
Log::debug('Now in getValidDestinationAccount().');
if (!$this->hasFields(['destination_id', 'destination_name'])) {
return $this->getOriginalDestinationAccount();
}
$value = $this->data['amount'] ?? '';
$destInfo = [
'id' => (int)($this->data['destination_id'] ?? null),
'name' => $this->data['destination_name'] ?? null,
'iban' => $this->data['destination_iban'] ?? null,
'number' => $this->data['destination_number'] ?? null,
'bic' => $this->data['destination_bic'] ?? null,
];
// make new account validator.
$expectedType = $this->getExpectedType();
Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType));
try {
$amount = $this->getAmount($value);
$result = $this->getAccount($expectedType, 'destination', $destInfo);
} catch (FireflyException $e) {
Log::debug(sprintf('getAmount("%s") returns error: %s', $value, $e->getMessage()));
Log::error(sprintf('getValidDestinationAccount() threw unexpected error: %s', $e->getMessage()));
$result = $this->getOriginalDestinationAccount();
}
return $result;
}
/**
* Updates journal transaction type.
*/
private function updateType(): void
{
Log::debug('Now in updateType()');
if ($this->hasFields(['type'])) {
$type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type'];
Log::debug(
sprintf(
'Trying to change journal #%d from a %s to a %s.',
$this->transactionJournal->id, $this->transactionJournal->transactionType->type, $type
)
);
/** @var TransactionTypeFactory $typeFactory */
$typeFactory = app(TransactionTypeFactory::class);
$result = $typeFactory->find($this->data['type']);
if (null !== $result) {
Log::debug('Changed transaction type!');
$this->transactionJournal->transaction_type_id = $result->id;
$this->transactionJournal->save();
return;
}
return;
}
$origSourceTransaction = $this->getSourceTransaction();
$origSourceTransaction->amount = app('steam')->negative($amount);
$origSourceTransaction->save();
$destTransaction = $this->getDestinationTransaction();
$destTransaction->amount = app('steam')->positive($amount);
$destTransaction->save();
// refresh transactions.
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
Log::debug(sprintf('Updated amount to "%s"', $amount));
Log::debug('No type field present.');
}
/**
@@ -479,6 +477,47 @@ class JournalUpdateService
}
}
/**
* Update journal generic field. Cannot be set to NULL.
*
* @param string $fieldName
*/
private function updateField(string $fieldName): void
{
if (array_key_exists($fieldName, $this->data) && '' !== (string)$this->data[$fieldName]) {
$value = $this->data[$fieldName];
if ('date' === $fieldName) {
if ($value instanceof Carbon) {
// update timezone.
$value->setTimezone(config('app.timezone'));
}
if (!($value instanceof Carbon)) {
$value = new Carbon($value);
}
// do some parsing.
Log::debug(sprintf('Create date value from string "%s".', $value));
}
$this->transactionJournal->$fieldName = $value;
Log::debug(sprintf('Updated %s', $fieldName));
}
}
/**
*
*/
private function updateCategory(): void
{
// update category
if ($this->hasFields(['category_id', 'category_name'])) {
Log::debug('Will update category.');
$this->storeCategory($this->transactionJournal, new NullArrayObject($this->data));
}
}
/**
*
*/
@@ -494,13 +533,103 @@ class JournalUpdateService
/**
*
*/
private function updateCategory(): void
private function updateTags(): void
{
// update category
if ($this->hasFields(['category_id', 'category_name'])) {
Log::debug('Will update category.');
if ($this->hasFields(['tags'])) {
Log::debug('Will update tags.');
$tags = $this->data['tags'] ?? null;
$this->storeTags($this->transactionJournal, $tags);
}
}
$this->storeCategory($this->transactionJournal, new NullArrayObject($this->data));
/**
*
*/
private function updateReconciled(): void
{
if (array_key_exists('reconciled', $this->data) && is_bool($this->data['reconciled'])) {
$this->transactionJournal->transactions()->update(['reconciled' => $this->data['reconciled']]);
}
}
/**
*
*/
private function updateNotes(): void
{
// update notes.
if ($this->hasFields(['notes'])) {
$notes = '' === (string)$this->data['notes'] ? null : $this->data['notes'];
$this->storeNotes($this->transactionJournal, $notes);
}
}
/**
*
*/
private function updateMeta(): void
{
// update meta fields.
// first string
if ($this->hasFields($this->metaString)) {
Log::debug('Meta string fields are present.');
$this->updateMetaFields();
}
// then date fields.
if ($this->hasFields($this->metaDate)) {
Log::debug('Meta date fields are present.');
$this->updateMetaDateFields();
}
}
/**
*
*/
private function updateMetaFields(): void
{
/** @var TransactionJournalMetaFactory $factory */
$factory = app(TransactionJournalMetaFactory::class);
foreach ($this->metaString as $field) {
if ($this->hasFields([$field])) {
$value = '' === $this->data[$field] ? null : $this->data[$field];
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
$set = [
'journal' => $this->transactionJournal,
'name' => $field,
'data' => $value,
];
$factory->updateOrCreate($set);
}
}
}
/**
*
*/
private function updateMetaDateFields(): void
{
/** @var TransactionJournalMetaFactory $factory */
$factory = app(TransactionJournalMetaFactory::class);
foreach ($this->metaDate as $field) {
if ($this->hasFields([$field])) {
try {
$value = '' === (string)$this->data[$field] ? null : new Carbon($this->data[$field]);
} catch (Exception $e) {
Log::debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage()));
return;
}
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
$set = [
'journal' => $this->transactionJournal,
'name' => $field,
'data' => $value,
];
$factory->updateOrCreate($set);
}
}
}
@@ -537,33 +666,37 @@ class JournalUpdateService
}
/**
* Update journal generic field. Cannot be set to NULL.
*
* @param string $fieldName
*/
private function updateField(string $fieldName): void
private function updateAmount(): void
{
if (array_key_exists($fieldName, $this->data) && '' !== (string)$this->data[$fieldName]) {
$value = $this->data[$fieldName];
if ('date' === $fieldName) {
if ($value instanceof Carbon) {
// update timezone.
$value->setTimezone(config('app.timezone'));
}
if (!($value instanceof Carbon)) {
$value = new Carbon($value);
}
// do some parsing.
Log::debug(sprintf('Create date value from string "%s".', $value));
}
$this->transactionJournal->$fieldName = $value;
Log::debug(sprintf('Updated %s', $fieldName));
if (!$this->hasFields(['amount'])) {
return;
}
}
$value = $this->data['amount'] ?? '';
try {
$amount = $this->getAmount($value);
} catch (FireflyException $e) {
Log::debug(sprintf('getAmount("%s") returns error: %s', $value, $e->getMessage()));
return;
}
$origSourceTransaction = $this->getSourceTransaction();
$origSourceTransaction->amount = app('steam')->negative($amount);
$origSourceTransaction->save();
$destTransaction = $this->getDestinationTransaction();
$destTransaction->amount = app('steam')->positive($amount);
$destTransaction->save();
// refresh transactions.
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
Log::debug(sprintf('Updated amount to "%s"', $amount));
}
/**
*
@@ -628,138 +761,4 @@ class JournalUpdateService
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
}
/**
*
*/
private function updateMeta(): void
{
// update meta fields.
// first string
if ($this->hasFields($this->metaString)) {
Log::debug('Meta string fields are present.');
$this->updateMetaFields();
}
// then date fields.
if ($this->hasFields($this->metaDate)) {
Log::debug('Meta date fields are present.');
$this->updateMetaDateFields();
}
}
/**
*
*/
private function updateMetaDateFields(): void
{
/** @var TransactionJournalMetaFactory $factory */
$factory = app(TransactionJournalMetaFactory::class);
foreach ($this->metaDate as $field) {
if ($this->hasFields([$field])) {
try {
$value = '' === (string)$this->data[$field] ? null : new Carbon($this->data[$field]);
} catch (Exception $e) {
Log::debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage()));
return;
}
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
$set = [
'journal' => $this->transactionJournal,
'name' => $field,
'data' => $value,
];
$factory->updateOrCreate($set);
}
}
}
/**
*
*/
private function updateMetaFields(): void
{
/** @var TransactionJournalMetaFactory $factory */
$factory = app(TransactionJournalMetaFactory::class);
foreach ($this->metaString as $field) {
if ($this->hasFields([$field])) {
$value = '' === $this->data[$field] ? null : $this->data[$field];
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
$set = [
'journal' => $this->transactionJournal,
'name' => $field,
'data' => $value,
];
$factory->updateOrCreate($set);
}
}
}
/**
*
*/
private function updateNotes(): void
{
// update notes.
if ($this->hasFields(['notes'])) {
$notes = '' === (string)$this->data['notes'] ? null : $this->data['notes'];
$this->storeNotes($this->transactionJournal, $notes);
}
}
/**
*
*/
private function updateTags(): void
{
if ($this->hasFields(['tags'])) {
Log::debug('Will update tags.');
$tags = $this->data['tags'] ?? null;
$this->storeTags($this->transactionJournal, $tags);
}
}
/**
* Updates journal transaction type.
*/
private function updateType(): void
{
Log::debug('Now in updateType()');
if ($this->hasFields(['type'])) {
$type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type'];
Log::debug(
sprintf(
'Trying to change journal #%d from a %s to a %s.',
$this->transactionJournal->id, $this->transactionJournal->transactionType->type, $type
)
);
/** @var TransactionTypeFactory $typeFactory */
$typeFactory = app(TransactionTypeFactory::class);
$result = $typeFactory->find($this->data['type']);
if (null !== $result) {
Log::debug('Changed transaction type!');
$this->transactionJournal->transaction_type_id = $result->id;
$this->transactionJournal->save();
return;
}
return;
}
Log::debug('No type field present.');
}
/**
*
*/
private function updateReconciled(): void
{
if (array_key_exists('reconciled', $this->data) && is_bool($this->data['reconciled'])) {
$this->transactionJournal->transactions()->update(['reconciled' => $this->data['reconciled']]);
}
}
}

View File

@@ -27,6 +27,7 @@ use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Note;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\RecurrenceRepetition;
use FireflyIII\Services\Internal\Support\RecurringTransactionTrait;
use FireflyIII\Services\Internal\Support\TransactionTypeTrait;
use FireflyIII\User;
@@ -56,53 +57,55 @@ class RecurrenceUpdateService
*/
public function update(Recurrence $recurrence, array $data): Recurrence
{
$this->user = $recurrence->user;
$transactionType = $recurrence->transactionType;
if (isset($data['recurrence']['type'])) {
$transactionType = $this->findTransactionType(ucfirst($data['recurrence']['type']));
}
$this->user = $recurrence->user;
// update basic fields first:
$recurrence->transaction_type_id = $transactionType->id;
$recurrence->title = $data['recurrence']['title'] ?? $recurrence->title;
$recurrence->description = $data['recurrence']['description'] ?? $recurrence->description;
$recurrence->first_date = $data['recurrence']['first_date'] ?? $recurrence->first_date;
$recurrence->repeat_until = $data['recurrence']['repeat_until'] ?? $recurrence->repeat_until;
$recurrence->repetitions = $data['recurrence']['nr_of_repetitions'] ?? $recurrence->repetitions;
$recurrence->apply_rules = $data['recurrence']['apply_rules'] ?? $recurrence->apply_rules;
$recurrence->active = $data['recurrence']['active'] ?? $recurrence->active;
// if nr_of_repetitions is set, then drop the "repeat_until" field.
if (0 !== $recurrence->repetitions) {
$recurrence->repeat_until = null;
}
if (isset($data['recurrence']['repetition_end'])) {
if (in_array($data['recurrence']['repetition_end'], ['forever', 'until_date'])) {
$recurrence->repetitions = 0;
if (array_key_exists('recurrence', $data)) {
$info = $data['recurrence'];
if (array_key_exists('title', $info)) {
$recurrence->title = $info['title'];
}
if (in_array($data['recurrence']['repetition_end'], ['forever', 'times'])) {
$recurrence->repeat_until = null;
if (array_key_exists('description', $info)) {
$recurrence->description = $info['description'];
}
if (array_key_exists('first_date', $info)) {
$recurrence->first_date = $info['first_date'];
}
if (array_key_exists('repeat_until', $info)) {
$recurrence->repeat_until = $info['repeat_until'];
$recurrence->repetitions = 0;
}
if (array_key_exists('nr_of_repetitions', $info)) {
if (0 !== (int)$info['nr_of_repetitions']) {
$recurrence->repeat_until = null;
}
$recurrence->repetitions = $info['nr_of_repetitions'];
}
if (array_key_exists('apply_rules', $info)) {
$recurrence->apply_rules = $info['apply_rules'];
}
if (array_key_exists('active', $info)) {
$recurrence->active = $info['active'];
}
// update all meta data:
if (array_key_exists('notes', $info)) {
$this->setNoteText($recurrence, $info['notes']);
}
}
$recurrence->save();
// update all meta data:
if (isset($data['recurrence']['notes']) && null !== $data['recurrence']['notes']) {
$this->setNoteText($recurrence, $data['recurrence']['notes']);
}
// update all repetitions
if (null !== $data['repetitions']) {
$this->deleteRepetitions($recurrence);
$this->createRepetitions($recurrence, $data['repetitions'] ?? []);
if (array_key_exists('repetitions', $data)) {
Log::debug('Will update repetitions array');
// update each repetition or throw error yay
$this->updateRepetitions($recurrence, $data['repetitions'] ?? []);
}
// update all transactions (and associated meta-data)
if (null !== $data['transactions']) {
$this->deleteTransactions($recurrence);
$this->createTransactions($recurrence, $data['transactions'] ?? []);
}
// // update all transactions (and associated meta-data)
// if (array_key_exists('transactions', $data)) {
// $this->deleteTransactions($recurrence);
// $this->createTransactions($recurrence, $data['transactions'] ?? []);
// }
return $recurrence;
}
@@ -133,4 +136,76 @@ class RecurrenceUpdateService
}
}
/**
*
* @param Recurrence $recurrence
* @param array $repetitions
*/
private function updateRepetitions(Recurrence $recurrence, array $repetitions): void
{
$originalCount = $recurrence->recurrenceRepetitions()->count();
if (0 === count($repetitions)) {
// wont drop repetition, rather avoid.
return;
}
// user added or removed repetitions, delete all and recreate:
if ($originalCount !== count($repetitions)) {
Log::debug('Del + recreate');
$this->deleteRepetitions($recurrence);
$this->createRepetitions($recurrence, $repetitions);
return;
}
// loop all and try to match them:
if ($originalCount === count($repetitions)) {
Log::debug('Loop and find');
foreach ($repetitions as $current) {
$match = $this->matchRepetition($recurrence, $current);
if (null === $match) {
throw new FireflyException('Cannot match recurring repetition to existing repetition. Not sure what to do. Break.');
}
$fields = [
'type' => 'repetition_type',
'moment' => 'repetition_moment',
'skip' => 'repetition_skip',
'weekend' => 'weekend',];
foreach ($fields as $field => $column) {
if (array_key_exists($field, $current)) {
$match->$column = $current[$field];
$match->save();
}
}
}
}
}
/**
* @param array $data
*
* @return RecurrenceRepetition|null
*/
private function matchRepetition(Recurrence $recurrence, array $data): ?RecurrenceRepetition
{
$originalCount = $recurrence->recurrenceRepetitions()->count();
if (1 === $originalCount) {
Log::debug('Return the first one');
return $recurrence->recurrenceRepetitions()->first();
}
// find it:
$fields = ['id' => 'id',
'type' => 'repetition_type',
'moment' => 'repetition_moment',
'skip' => 'repetition_skip',
'weekend' => 'weekend',
];
$query = $recurrence->recurrenceRepetitions();
foreach ($fields as $field => $column) {
if (array_key_exists($field, $data)) {
$query->where($column, $data[$field]);
}
}
return $query->first();
}
}