feat: update all rules to support action value expressions

This commit is contained in:
Michael Thomas
2024-03-06 20:54:50 -05:00
parent daddee7806
commit 95593f847b
17 changed files with 236 additions and 151 deletions

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -29,6 +30,7 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Factory\TagFactory; use FireflyIII\Factory\TagFactory;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -38,15 +40,17 @@ use Illuminate\Support\Facades\Log;
class AddTag implements ActionInterface class AddTag implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -58,11 +62,12 @@ class AddTag implements ActionInterface
/** @var TagFactory $factory */ /** @var TagFactory $factory */
$factory = app(TagFactory::class); $factory = app(TagFactory::class);
$factory->setUser(User::find($journal['user_id'])); $factory->setUser(User::find($journal['user_id']));
$tag = $factory->findOrCreate($this->action->action_value); $tagName = $this->evaluator->evaluate($journal);
$tag = $factory->findOrCreate($tagName);
if (null === $tag) { if (null === $tag) {
// could not find, could not create tag. // could not find, could not create tag.
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.find_or_create_tag_failed', ['tag' => $this->action->action_value]))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.find_or_create_tag_failed', ['tag' => $tagName])));
return false; return false;
} }
@@ -84,7 +89,7 @@ class AddTag implements ActionInterface
Log::debug( Log::debug(
sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id']) sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $this->action->action_value]))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $tagName])));
return false; return false;
} }

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -27,6 +28,7 @@ use DB;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
/** /**
* Class AppendDescription. * Class AppendDescription.
@@ -34,15 +36,17 @@ use FireflyIII\Models\TransactionJournal;
class AppendDescription implements ActionInterface class AppendDescription implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -50,7 +54,8 @@ class AppendDescription implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$description = sprintf('%s%s', $journal['description'], $this->action->action_value); $actionValue = $this->evaluator->evaluate($journal);
$description = sprintf('%s%s', $journal['description'], $actionValue);
DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]); DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]);
// event for audit log entry // event for audit log entry

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -27,6 +28,7 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@@ -35,15 +37,17 @@ use Illuminate\Support\Facades\Log;
class AppendNotes implements ActionInterface class AppendNotes implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -51,6 +55,7 @@ class AppendNotes implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$actionValue = $this->evaluator->evaluate($journal);
$dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id'])
->where('noteable_type', TransactionJournal::class) ->where('noteable_type', TransactionJournal::class)
->first(['notes.*']); ->first(['notes.*']);
@@ -60,9 +65,9 @@ class AppendNotes implements ActionInterface
$dbNote->noteable_type = TransactionJournal::class; $dbNote->noteable_type = TransactionJournal::class;
$dbNote->text = ''; $dbNote->text = '';
} }
Log::debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $this->action->action_value, $dbNote->text)); Log::debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $actionValue, $dbNote->text));
$before = $dbNote->text; $before = $dbNote->text;
$text = sprintf('%s%s', $dbNote->text, $this->action->action_value); $text = sprintf('%s%s', $dbNote->text, $actionValue);
$dbNote->text = $text; $dbNote->text = $text;
$dbNote->save(); $dbNote->save();

View File

@@ -1,4 +1,5 @@
<?php <?php
/** /**
* ConvertToDeposit.php * ConvertToDeposit.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org
@@ -35,6 +36,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use JsonException; use JsonException;
@@ -45,15 +47,17 @@ use JsonException;
class ConvertToDeposit implements ActionInterface class ConvertToDeposit implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -61,6 +65,8 @@ class ConvertToDeposit implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$actionValue = $this->evaluator->evaluate($journal);
// make object from array (so the data is fresh). // make object from array (so the data is fresh).
/** @var TransactionJournal|null $object */ /** @var TransactionJournal|null $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
@@ -88,7 +94,7 @@ class ConvertToDeposit implements ActionInterface
Log::debug('Going to transform a withdrawal to a deposit.'); Log::debug('Going to transform a withdrawal to a deposit.');
try { try {
$res = $this->convertWithdrawalArray($object); $res = $this->convertWithdrawalArray($object, $actionValue);
} catch (JsonException | FireflyException $e) { } catch (JsonException | FireflyException $e) {
Log::debug('Could not convert withdrawal to deposit.'); Log::debug('Could not convert withdrawal to deposit.');
Log::error($e->getMessage()); Log::error($e->getMessage());
@@ -129,7 +135,7 @@ class ConvertToDeposit implements ActionInterface
* @throws FireflyException * @throws FireflyException
* @throws JsonException * @throws JsonException
*/ */
private function convertWithdrawalArray(TransactionJournal $journal): bool private function convertWithdrawalArray(TransactionJournal $journal, string $actionValue): bool
{ {
$user = $journal->user; $user = $journal->user;
// find or create revenue account. // find or create revenue account.
@@ -145,7 +151,7 @@ class ConvertToDeposit implements ActionInterface
// get the action value, or use the original destination name in case the action value is empty: // get the action value, or use the original destination name in case the action value is empty:
// this becomes a new or existing (revenue) account, which is the source of the new deposit. // this becomes a new or existing (revenue) account, which is the source of the new deposit.
$opposingName = '' === $this->action->action_value ? $destAccount->name : $this->action->action_value; $opposingName = '' === $actionValue ? $destAccount->name : $actionValue;
// we check all possible source account types if one exists: // we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.source.Deposit'); $validTypes = config('firefly.expected_source_types.source.Deposit');
$opposingAccount = $repository->findByName($opposingName, $validTypes); $opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -153,7 +159,7 @@ class ConvertToDeposit implements ActionInterface
$opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE); $opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE);
} }
Log::debug(sprintf('ConvertToDeposit. Action value is "%s", new opposing name is "%s"', $this->action->action_value, $opposingAccount->name)); Log::debug(sprintf('ConvertToDeposit. Action value is "%s", new opposing name is "%s"', $actionValue, $opposingAccount->name));
// update the source transaction and put in the new revenue ID. // update the source transaction and put in the new revenue ID.
DB::table('transactions') DB::table('transactions')
@@ -237,7 +243,7 @@ class ConvertToDeposit implements ActionInterface
// get the action value, or use the original source name in case the action value is empty: // get the action value, or use the original source name in case the action value is empty:
// this becomes a new or existing (revenue) account, which is the source of the new deposit. // this becomes a new or existing (revenue) account, which is the source of the new deposit.
$opposingName = '' === $this->action->action_value ? $sourceAccount->name : $this->action->action_value; $opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue;
// we check all possible source account types if one exists: // we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.source.Deposit'); $validTypes = config('firefly.expected_source_types.source.Deposit');
$opposingAccount = $repository->findByName($opposingName, $validTypes); $opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -245,7 +251,7 @@ class ConvertToDeposit implements ActionInterface
$opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE); $opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE);
} }
Log::debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $this->action->action_value, $opposingAccount->name)); Log::debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $actionValue, $opposingAccount->name));
// update source transaction(s) to be revenue account // update source transaction(s) to be revenue account
DB::table('transactions') DB::table('transactions')

View File

@@ -1,4 +1,5 @@
<?php <?php
/** /**
* ConvertToTransfer.php * ConvertToTransfer.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org
@@ -34,6 +35,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@@ -43,15 +45,17 @@ use Illuminate\Support\Facades\Log;
class ConvertToTransfer implements ActionInterface class ConvertToTransfer implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -59,6 +63,8 @@ class ConvertToTransfer implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$actionValue = $this->evaluator->evaluate($journal);
// make object from array (so the data is fresh). // make object from array (so the data is fresh).
/** @var TransactionJournal|null $object */ /** @var TransactionJournal|null $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
@@ -103,7 +109,7 @@ class ConvertToTransfer implements ActionInterface
$expectedType = $this->getDestinationType($journalId); $expectedType = $this->getDestinationType($journalId);
// Deposit? Replace source with account with same type as destination. // Deposit? Replace source with account with same type as destination.
} }
$opposing = $repository->findByName($this->action->action_value, [$expectedType]); $opposing = $repository->findByName($actionValue, [$expectedType]);
if (null === $opposing) { if (null === $opposing) {
Log::error( Log::error(
@@ -111,11 +117,11 @@ class ConvertToTransfer implements ActionInterface
'Journal #%d cannot be converted because no valid %s account with name "%s" exists (rule #%d).', 'Journal #%d cannot be converted because no valid %s account with name "%s" exists (rule #%d).',
$expectedType, $expectedType,
$journalId, $journalId,
$this->action->action_value, $actionValue,
$this->action->rule_id $this->action->rule_id
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $this->action->action_value]))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $actionValue])));
return false; return false;
} }

View File

@@ -1,4 +1,5 @@
<?php <?php
/** /**
* ConvertToWithdrawal.php * ConvertToWithdrawal.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org
@@ -35,6 +36,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use JsonException; use JsonException;
@@ -45,15 +47,17 @@ use JsonException;
class ConvertToWithdrawal implements ActionInterface class ConvertToWithdrawal implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -61,6 +65,8 @@ class ConvertToWithdrawal implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$actionValue = $this->evaluator->evaluate($journal);
// make object from array (so the data is fresh). // make object from array (so the data is fresh).
/** @var TransactionJournal|null $object */ /** @var TransactionJournal|null $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
@@ -89,7 +95,7 @@ class ConvertToWithdrawal implements ActionInterface
if (TransactionType::DEPOSIT === $type) { if (TransactionType::DEPOSIT === $type) {
Log::debug('Going to transform a deposit to a withdrawal.'); Log::debug('Going to transform a deposit to a withdrawal.');
try { try {
$res = $this->convertDepositArray($object); $res = $this->convertDepositArray($object, $actionValue);
} catch (JsonException | FireflyException $e) { } catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.'); Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage()); Log::error($e->getMessage());
@@ -104,7 +110,7 @@ class ConvertToWithdrawal implements ActionInterface
Log::debug('Going to transform a transfer to a withdrawal.'); Log::debug('Going to transform a transfer to a withdrawal.');
try { try {
$res = $this->convertTransferArray($object); $res = $this->convertTransferArray($object, $actionValue);
} catch (JsonException | FireflyException $e) { } catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.'); Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage()); Log::error($e->getMessage());
@@ -126,7 +132,7 @@ class ConvertToWithdrawal implements ActionInterface
* @throws FireflyException * @throws FireflyException
* @throws JsonException * @throws JsonException
*/ */
private function convertDepositArray(TransactionJournal $journal): bool private function convertDepositArray(TransactionJournal $journal, string $actionValue): bool
{ {
$user = $journal->user; $user = $journal->user;
/** @var AccountFactory $factory */ /** @var AccountFactory $factory */
@@ -141,7 +147,7 @@ class ConvertToWithdrawal implements ActionInterface
// get the action value, or use the original source name in case the action value is empty: // get the action value, or use the original source name in case the action value is empty:
// this becomes a new or existing (expense) account, which is the destination of the new withdrawal. // this becomes a new or existing (expense) account, which is the destination of the new withdrawal.
$opposingName = '' === $this->action->action_value ? $sourceAccount->name : $this->action->action_value; $opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue;
// we check all possible source account types if one exists: // we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.destination.Withdrawal'); $validTypes = config('firefly.expected_source_types.destination.Withdrawal');
$opposingAccount = $repository->findByName($opposingName, $validTypes); $opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -149,7 +155,7 @@ class ConvertToWithdrawal implements ActionInterface
$opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE); $opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE);
} }
Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $this->action->action_value, $opposingName)); Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $actionValue, $opposingName));
// update source transaction(s) to be the original destination account // update source transaction(s) to be the original destination account
DB::table('transactions') DB::table('transactions')
@@ -216,7 +222,7 @@ class ConvertToWithdrawal implements ActionInterface
* @throws FireflyException * @throws FireflyException
* @throws JsonException * @throws JsonException
*/ */
private function convertTransferArray(TransactionJournal $journal): bool private function convertTransferArray(TransactionJournal $journal, string $actionValue): bool
{ {
// find or create expense account. // find or create expense account.
$user = $journal->user; $user = $journal->user;
@@ -231,7 +237,7 @@ class ConvertToWithdrawal implements ActionInterface
// get the action value, or use the original source name in case the action value is empty: // get the action value, or use the original source name in case the action value is empty:
// this becomes a new or existing (expense) account, which is the destination of the new withdrawal. // this becomes a new or existing (expense) account, which is the destination of the new withdrawal.
$opposingName = '' === $this->action->action_value ? $destAccount->name : $this->action->action_value; $opposingName = '' === $actionValue ? $destAccount->name : $actionValue;
// we check all possible source account types if one exists: // we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.destination.Withdrawal'); $validTypes = config('firefly.expected_source_types.destination.Withdrawal');
$opposingAccount = $repository->findByName($opposingName, $validTypes); $opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -239,7 +245,7 @@ class ConvertToWithdrawal implements ActionInterface
$opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE); $opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE);
} }
Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", destination name is "%s"', $this->action->action_value, $opposingName)); Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", destination name is "%s"', $actionValue, $opposingName));
// update destination transaction(s) to be new expense account. // update destination transaction(s) to be new expense account.
DB::table('transactions') DB::table('transactions')

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -30,6 +31,7 @@ use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -39,6 +41,7 @@ use Illuminate\Support\Facades\Log;
class LinkToBill implements ActionInterface class LinkToBill implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
@@ -46,9 +49,10 @@ class LinkToBill implements ActionInterface
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -60,7 +64,7 @@ class LinkToBill implements ActionInterface
/** @var BillRepositoryInterface $repository */ /** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class); $repository = app(BillRepositoryInterface::class);
$repository->setUser($user); $repository->setUser($user);
$billName = (string)$this->action->action_value; $billName = $this->evaluator->evaluate($journal);
$bill = $repository->findByName($billName); $bill = $repository->findByName($billName);
if (null !== $bill && $journal['transaction_type_type'] === TransactionType::WITHDRAWAL) { if (null !== $bill && $journal['transaction_type_type'] === TransactionType::WITHDRAWAL) {

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -27,6 +28,7 @@ use DB;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
/** /**
* Class PrependDescription. * Class PrependDescription.
@@ -34,15 +36,17 @@ use FireflyIII\Models\TransactionJournal;
class PrependDescription implements ActionInterface class PrependDescription implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -50,8 +54,10 @@ class PrependDescription implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$actionValue = $this->evaluator->evaluate($journal);
$before = $journal['description']; $before = $journal['description'];
$after = sprintf('%s%s', $this->action->action_value, $journal['description']); $after = sprintf('%s%s', $actionValue, $journal['description']);
DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $after]); DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $after]);
// journal // journal

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -27,6 +28,7 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@@ -35,15 +37,17 @@ use Illuminate\Support\Facades\Log;
class PrependNotes implements ActionInterface class PrependNotes implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -51,6 +55,8 @@ class PrependNotes implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$actionValue = $this->evaluator->evaluate($journal);
$dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id'])
->where('noteable_type', TransactionJournal::class) ->where('noteable_type', TransactionJournal::class)
->first(['notes.*']); ->first(['notes.*']);
@@ -61,8 +67,8 @@ class PrependNotes implements ActionInterface
$dbNote->text = ''; $dbNote->text = '';
} }
$before = $dbNote->text; $before = $dbNote->text;
Log::debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $this->action->action_value, $dbNote->text)); Log::debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $actionValue, $dbNote->text));
$text = sprintf('%s%s', $this->action->action_value, $dbNote->text); $text = sprintf('%s%s', $actionValue, $dbNote->text);
$dbNote->text = $text; $dbNote->text = $text;
$dbNote->save(); $dbNote->save();

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -28,6 +29,7 @@ use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -37,15 +39,17 @@ use Illuminate\Support\Facades\Log;
class RemoveTag implements ActionInterface class RemoveTag implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -54,7 +58,7 @@ class RemoveTag implements ActionInterface
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
// if tag does not exist, no need to continue: // if tag does not exist, no need to continue:
$name = $this->action->action_value; $name = $this->evaluator->evaluate($journal);
$user = User::find($journal['user_id']); $user = User::find($journal['user_id']);
$tag = $user->tags()->where('tag', $name)->first(); $tag = $user->tags()->where('tag', $name)->first();

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -29,6 +30,7 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -38,15 +40,17 @@ use Illuminate\Support\Facades\Log;
class SetBudget implements ActionInterface class SetBudget implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -55,7 +59,7 @@ class SetBudget implements ActionInterface
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$user = User::find($journal['user_id']); $user = User::find($journal['user_id']);
$search = $this->action->action_value; $search = $this->evaluator->evaluate($journal);
$budget = $user->budgets()->where('name', $search)->first(); $budget = $user->budgets()->where('name', $search)->first();
if (null === $budget) { if (null === $budget) {

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -29,6 +30,7 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Factory\CategoryFactory; use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -38,15 +40,17 @@ use Illuminate\Support\Facades\Log;
class SetCategory implements ActionInterface class SetCategory implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -55,7 +59,7 @@ class SetCategory implements ActionInterface
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$user = User::find($journal['user_id']); $user = User::find($journal['user_id']);
$search = $this->action->action_value; $search = $this->evaluator->evaluate($journal);
if (null === $user) { if (null === $user) {
Log::error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); Log::error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -27,6 +28,7 @@ use DB;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@@ -35,15 +37,17 @@ use Illuminate\Support\Facades\Log;
class SetDescription implements ActionInterface class SetDescription implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -53,22 +57,23 @@ class SetDescription implements ActionInterface
{ {
/** @var TransactionJournal $object */ /** @var TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$before = $object->description; $before = $journal['description'];
$after = $this->evaluator->evaluate($journal);
DB::table('transaction_journals') DB::table('transaction_journals')
->where('id', '=', $journal['transaction_journal_id']) ->where('id', '=', $journal['transaction_journal_id'])
->update(['description' => $this->action->action_value]); ->update(['description' => $after]);
Log::debug( Log::debug(
sprintf( sprintf(
'RuleAction SetDescription changed the description of journal #%d from "%s" to "%s".', 'RuleAction SetDescription changed the description of journal #%d from "%s" to "%s".',
$journal['transaction_journal_id'], $journal['transaction_journal_id'],
$journal['description'], $before,
$this->action->action_value $after
) )
); );
$object->refresh(); $object->refresh();
event(new TriggeredAuditLog($this->action->rule, $object, 'update_description', $before, $this->action->action_value)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_description', $before, $after));
return true; return true;
} }

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -32,6 +33,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -41,6 +43,7 @@ use Illuminate\Support\Facades\Log;
class SetDestinationAccount implements ActionInterface class SetDestinationAccount implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
private AccountRepositoryInterface $repository; private AccountRepositoryInterface $repository;
/** /**
@@ -48,9 +51,10 @@ class SetDestinationAccount implements ActionInterface
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -58,6 +62,7 @@ class SetDestinationAccount implements ActionInterface
*/ */
public function actOnArray(array $journal): bool public function actOnArray(array $journal): bool
{ {
$accountName = $this->evaluator->evaluate($journal);
$user = User::find($journal['user_id']); $user = User::find($journal['user_id']);
$type = $journal['transaction_type_type']; $type = $journal['transaction_type_type'];
/** @var TransactionJournal|null $object */ /** @var TransactionJournal|null $object */
@@ -73,16 +78,16 @@ class SetDestinationAccount implements ActionInterface
$this->repository->setUser($user); $this->repository->setUser($user);
// if this is a transfer or a deposit, the new destination account must be an asset account or a default account, and it MUST exist: // if this is a transfer or a deposit, the new destination account must be an asset account or a default account, and it MUST exist:
$newAccount = $this->findAssetAccount($type); $newAccount = $this->findAssetAccount($type, $accountName);
if ((TransactionType::DEPOSIT === $type || TransactionType::TRANSFER === $type) && null === $newAccount) { if ((TransactionType::DEPOSIT === $type || TransactionType::TRANSFER === $type) && null === $newAccount) {
Log::error( Log::error(
sprintf( sprintf(
'Cant change destination account of journal #%d because no asset account with name "%s" exists.', 'Cant change destination account of journal #%d because no asset account with name "%s" exists.',
$object->id, $object->id,
$this->action->action_value $accountName
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value]))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $accountName])));
return false; return false;
} }
@@ -116,7 +121,7 @@ class SetDestinationAccount implements ActionInterface
// if this is a withdrawal, the new destination account must be a expense account and may be created: // if this is a withdrawal, the new destination account must be a expense account and may be created:
// or it is a liability, in which case it must be returned. // or it is a liability, in which case it must be returned.
if (TransactionType::WITHDRAWAL === $type) { if (TransactionType::WITHDRAWAL === $type) {
$newAccount = $this->findWithdrawalDestinationAccount(); $newAccount = $this->findWithdrawalDestinationAccount($accountName);
} }
Log::debug(sprintf('New destination account is #%d ("%s").', $newAccount->id, $newAccount->name)); Log::debug(sprintf('New destination account is #%d ("%s").', $newAccount->id, $newAccount->name));
@@ -139,26 +144,26 @@ class SetDestinationAccount implements ActionInterface
* *
* @return Account|null * @return Account|null
*/ */
private function findAssetAccount(string $type): ?Account private function findAssetAccount(string $type, string $accountName): ?Account
{ {
// switch on type: // switch on type:
$allowed = config(sprintf('firefly.expected_source_types.destination.%s', $type)); $allowed = config(sprintf('firefly.expected_source_types.destination.%s', $type));
$allowed = is_array($allowed) ? $allowed : []; $allowed = is_array($allowed) ? $allowed : [];
Log::debug(sprintf('Check config for expected_source_types.destination.%s, result is', $type), $allowed); Log::debug(sprintf('Check config for expected_source_types.destination.%s, result is', $type), $allowed);
return $this->repository->findByName($this->action->action_value, $allowed); return $this->repository->findByName($accountName, $allowed);
} }
/** /**
* @return Account * @return Account
*/ */
private function findWithdrawalDestinationAccount(): Account private function findWithdrawalDestinationAccount(string $accountName): Account
{ {
$allowed = config('firefly.expected_source_types.destination.Withdrawal'); $allowed = config('firefly.expected_source_types.destination.Withdrawal');
$account = $this->repository->findByName($this->action->action_value, $allowed); $account = $this->repository->findByName($accountName, $allowed);
if (null === $account) { if (null === $account) {
$data = [ $data = [
'name' => $this->action->action_value, 'name' => $accountName,
'account_type_name' => 'expense', 'account_type_name' => 'expense',
'account_type_id' => null, 'account_type_id' => null,
'virtual_balance' => 0, 'virtual_balance' => 0,

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -27,6 +28,7 @@ use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@@ -35,15 +37,17 @@ use Illuminate\Support\Facades\Log;
class SetNotes implements ActionInterface class SetNotes implements ActionInterface
{ {
private RuleACtion $action; private RuleACtion $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -60,7 +64,8 @@ class SetNotes implements ActionInterface
$dbNote->text = ''; $dbNote->text = '';
} }
$oldNotes = $dbNote->text; $oldNotes = $dbNote->text;
$dbNote->text = $this->action->action_value; $newNotes = $this->evaluator->evaluate($journal);
$dbNote->text = $newNotes;
$dbNote->save(); $dbNote->save();
Log::debug( Log::debug(
@@ -68,14 +73,14 @@ class SetNotes implements ActionInterface
'RuleAction SetNotes changed the notes of journal #%d from "%s" to "%s".', 'RuleAction SetNotes changed the notes of journal #%d from "%s" to "%s".',
$journal['transaction_journal_id'], $journal['transaction_journal_id'],
$oldNotes, $oldNotes,
$this->action->action_value $newNotes
) )
); );
/** @var TransactionJournal $object */ /** @var TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
event(new TriggeredAuditLog($this->action->rule, $object, 'update_notes', $oldNotes, $this->action->action_value)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_notes', $oldNotes, $newNotes));
return true; return true;
} }

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
@@ -32,6 +33,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -41,6 +43,7 @@ use Illuminate\Support\Facades\Log;
class SetSourceAccount implements ActionInterface class SetSourceAccount implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
private AccountRepositoryInterface $repository; private AccountRepositoryInterface $repository;
/** /**
@@ -48,9 +51,10 @@ class SetSourceAccount implements ActionInterface
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -60,6 +64,7 @@ class SetSourceAccount implements ActionInterface
{ {
$user = User::find($journal['user_id']); $user = User::find($journal['user_id']);
$type = $journal['transaction_type_type']; $type = $journal['transaction_type_type'];
$name = $this->evaluator->evaluate($journal);
/** @var TransactionJournal|null $object */ /** @var TransactionJournal|null $object */
$object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']);
$this->repository = app(AccountRepositoryInterface::class); $this->repository = app(AccountRepositoryInterface::class);
@@ -72,12 +77,12 @@ class SetSourceAccount implements ActionInterface
$this->repository->setUser($user); $this->repository->setUser($user);
// if this is a transfer or a withdrawal, the new source account must be an asset account or a default account, and it MUST exist: // if this is a transfer or a withdrawal, the new source account must be an asset account or a default account, and it MUST exist:
$newAccount = $this->findAssetAccount($type); $newAccount = $this->findAssetAccount($type, $name);
if ((TransactionType::WITHDRAWAL === $type || TransactionType::TRANSFER === $type) && null === $newAccount) { if ((TransactionType::WITHDRAWAL === $type || TransactionType::TRANSFER === $type) && null === $newAccount) {
Log::error( Log::error(
sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $this->action->action_value) sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $name)
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value]))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $name])));
return false; return false;
} }
@@ -110,7 +115,7 @@ class SetSourceAccount implements ActionInterface
// if this is a deposit, the new source account must be a revenue account and may be created: // if this is a deposit, the new source account must be a revenue account and may be created:
// or it's a liability // or it's a liability
if (TransactionType::DEPOSIT === $type) { if (TransactionType::DEPOSIT === $type) {
$newAccount = $this->findDepositSourceAccount(); $newAccount = $this->findDepositSourceAccount($name);
} }
Log::debug(sprintf('New source account is #%d ("%s").', $newAccount->id, $newAccount->name)); Log::debug(sprintf('New source account is #%d ("%s").', $newAccount->id, $newAccount->name));
@@ -133,27 +138,27 @@ class SetSourceAccount implements ActionInterface
* *
* @return Account|null * @return Account|null
*/ */
private function findAssetAccount(string $type): ?Account private function findAssetAccount(string $type, string $name): ?Account
{ {
// switch on type: // switch on type:
$allowed = config(sprintf('firefly.expected_source_types.source.%s', $type)); $allowed = config(sprintf('firefly.expected_source_types.source.%s', $type));
$allowed = is_array($allowed) ? $allowed : []; $allowed = is_array($allowed) ? $allowed : [];
Log::debug(sprintf('Check config for expected_source_types.source.%s, result is', $type), $allowed); Log::debug(sprintf('Check config for expected_source_types.source.%s, result is', $type), $allowed);
return $this->repository->findByName($this->action->action_value, $allowed); return $this->repository->findByName($name, $allowed);
} }
/** /**
* @return Account * @return Account
*/ */
private function findDepositSourceAccount(): Account private function findDepositSourceAccount(string $name): Account
{ {
$allowed = config('firefly.expected_source_types.source.Deposit'); $allowed = config('firefly.expected_source_types.source.Deposit');
$account = $this->repository->findByName($this->action->action_value, $allowed); $account = $this->repository->findByName($name, $allowed);
if (null === $account) { if (null === $account) {
// create new revenue account with this name: // create new revenue account with this name:
$data = [ $data = [
'name' => $this->action->action_value, 'name' => $name,
'account_type_name' => 'revenue', 'account_type_name' => 'revenue',
'account_type_id' => null, 'account_type_id' => null,
'virtual_balance' => 0, 'virtual_balance' => 0,

View File

@@ -32,6 +32,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -41,15 +42,17 @@ use Illuminate\Support\Facades\Log;
class UpdatePiggybank implements ActionInterface class UpdatePiggybank implements ActionInterface
{ {
private RuleAction $action; private RuleAction $action;
private ActionExpressionEvaluator $evaluator;
/** /**
* TriggerInterface constructor. * TriggerInterface constructor.
* *
* @param RuleAction $action * @param RuleAction $action
*/ */
public function __construct(RuleAction $action) public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator)
{ {
$this->action = $action; $this->action = $action;
$this->evaluator = $evaluator;
} }
/** /**
@@ -59,18 +62,19 @@ class UpdatePiggybank implements ActionInterface
{ {
Log::debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); Log::debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id']));
$piggyBankName = $this->evaluator->evaluate($journal);
// refresh the transaction type. // refresh the transaction type.
$user = User::find($journal['user_id']); $user = User::find($journal['user_id']);
/** @var TransactionJournal $journalObj */ /** @var TransactionJournal $journalObj */
$journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']);
$type = TransactionType::find((int)$journalObj->transaction_type_id); $type = TransactionType::find((int)$journalObj->transaction_type_id);
$piggyBank = $this->findPiggyBank($user); $piggyBank = $this->findPiggyBank($user, $piggyBankName);
if (null === $piggyBank) { if (null === $piggyBank) {
Log::info( Log::info(
sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $this->action->action_value, $this->action->id, $this->action->rule_id) sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $piggyBankName, $this->action->id, $this->action->rule_id)
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $this->action->action_value]))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $piggyBankName])));
return false; return false;
} }
@@ -130,7 +134,7 @@ class UpdatePiggybank implements ActionInterface
$destination->account_id $destination->account_id
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $this->action->action_value]))); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $piggyBankName])));
return false; return false;
} }
@@ -139,9 +143,9 @@ class UpdatePiggybank implements ActionInterface
* *
* @return PiggyBank|null * @return PiggyBank|null
*/ */
private function findPiggyBank(User $user): ?PiggyBank private function findPiggyBank(User $user, string $name): ?PiggyBank
{ {
return $user->piggyBanks()->where('piggy_banks.name', $this->action->action_value)->first(); return $user->piggyBanks()->where('piggy_banks.name', $name)->first();
} }
/** /**