Replace transaction collector.

This commit is contained in:
James Cole 2019-05-29 18:28:28 +02:00
parent 627ef09f11
commit d13317095f
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
10 changed files with 648 additions and 220 deletions

View File

@ -0,0 +1,381 @@
<?php
namespace FireflyIII\Console\Commands\Tools;
use Carbon\Carbon;
use FireflyIII\Console\Commands\VerifiesAccessToken;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\TransactionRules\Processor;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Log;
/**
* Class ApplyRules
*/
class ApplyRules extends Command
{
use VerifiesAccessToken;
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command will apply your rules and rule groups on a selection of your transactions.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature
= 'firefly-iii:apply-rules
{--user=1 : The user ID that the import should import for.}
{--token= : The user\'s access token.}
{--accounts= : A comma-separated list of asset accounts or liabilities to apply your rules to.}
{--rule_groups= : A comma-separated list of rule groups to apply. Take the ID\'s of these rule groups from the Firefly III interface.}
{--rules= : A comma-separated list of rules to apply. Take the ID\'s of these rules from the Firefly III interface. Using this option overrules the option that selects rule groups.}
{--all_rules : If set, will overrule both settings and simply apply ALL of your rules.}
{--start_date= : The date of the earliest transaction to be included (inclusive). If omitted, will be your very first transaction ever. Format: YYYY-MM-DD}
{--end_date= : The date of the latest transaction to be included (inclusive). If omitted, will be your latest transaction ever. Format: YYYY-MM-DD}';
/** @var Collection */
private $accounts;
/** @var array */
private $acceptedAccounts;
/** @var Carbon */
private $endDate;
/** @var Collection */
private $results;
/** @var array */
private $ruleGroupSelection;
/** @var array */
private $ruleSelection;
/** @var Carbon */
private $startDate;
/** @var Collection */
private $groups;
/** @var bool */
private $allRules;
/** @var RuleRepositoryInterface */
private $ruleRepository;
/** @var RuleGroupRepositoryInterface */
private $ruleGroupRepository;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->allRules = false;
$this->accounts = new Collection;
$this->ruleSelection = [];
$this->ruleGroupSelection = [];
$this->results = new Collection;
$this->ruleRepository = app(RuleRepositoryInterface::class);
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
$this->acceptedAccounts = [AccountType::DEFAULT, AccountType::DEBT, AccountType::ASSET, AccountType::LOAN, AccountType::MORTGAGE];
$this->groups = new Collection;
}
/**
* Execute the console command.
*
* @return int
* @throws FireflyException
*/
public function handle(): int
{
if (!$this->verifyAccessToken()) {
$this->error('Invalid access token.');
return 1;
}
// set user:
$this->ruleRepository->setUser($this->getUser());
$this->ruleGroupRepository->setUser($this->getUser());
$result = $this->verifyInput();
if (false === $result) {
return 1;
}
$this->allRules = $this->option('all_rules');
$this->grabAllRules();
// loop all groups and rules and indicate if they're included:
$count = 0;
/** @var RuleGroup $group */
foreach ($this->groups as $group) {
/** @var Rule $rule */
foreach ($group->rules as $rule) {
// if in rule selection, or group in selection or all rules, it's included.
if ($this->includeRule($rule, $group)) {
$count++;
}
}
}
if (0 === $count) {
$this->error('No rules or rule groups have been included.');
$this->warn('Make a selection using:');
$this->warn(' --rules=1,2,...');
$this->warn(' --rule_groups=1,2,...');
$this->warn(' --all_rules');
}
// get transactions from asset accounts.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->getUser());
$collector->setAccounts($this->accounts);
$collector->setRange($this->startDate, $this->endDate);
$journals = $collector->getExtractedJournals();
// start running rules.
$this->line(sprintf('Will apply %d rules to %d transactions.', $count, count($journals)));
// start looping.
$bar = $this->output->createProgressBar(count($journals) * $count);
Log::debug(sprintf('Now looping %d transactions.', count($journals)));
/** @var array $journal */
foreach ($journals as $journal) {
Log::debug('Start of new journal.');
foreach ($this->groups as $group) {
$groupTriggered = false;
/** @var Rule $rule */
foreach ($group->rules as $rule) {
$ruleTriggered = false;
// if in rule selection, or group in selection or all rules, it's included.
if ($this->includeRule($rule, $group)) {
/** @var Processor $processor */
$processor = app(Processor::class);
$processor->make($rule, true);
$ruleTriggered = $processor->handleJournalArray($journal);
$bar->advance();
if ($ruleTriggered) {
$groupTriggered = true;
}
}
// if the rule is triggered and stop processing is true, cancel the entire group.
if ($ruleTriggered && $rule->stop_processing) {
Log::info('Break out group because rule was triggered.');
break;
}
}
// if group is triggered and stop processing is true, cancel the whole thing.
if ($groupTriggered && $group->stop_processing) {
Log::info('Break out ALL because group was triggered.');
break;
}
}
Log::debug('Done with all rules for this group + done with journal.');
}
$this->line('');
$this->line('Done!');
return 0;
}
/**
* @return bool
* @throws FireflyException
*/
private function verifyInput(): bool
{
// verify account.
$result = $this->verifyInputAccounts();
if (false === $result) {
return $result;
}
// verify rule groups.
$result = $this->verifyInputRuleGroups();
if (false === $result) {
return $result;
}
// verify rules.
$result = $this->verifyInputRules();
if (false === $result) {
return $result;
}
$this->verifyInputDates();
return true;
}
/**
* @return bool
* @throws FireflyException
*/
private function verifyInputAccounts(): bool
{
$accountString = $this->option('accounts');
if (null === $accountString || '' === $accountString) {
$this->error('Please use the --accounts option to indicate the accounts to apply rules to.');
return false;
}
$finalList = new Collection;
$accountList = explode(',', $accountString);
if (0 === count($accountList)) {
$this->error('Please use the --accounts option to indicate the accounts to apply rules to.');
return false;
}
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accountRepository->setUser($this->getUser());
foreach ($accountList as $accountId) {
$accountId = (int)$accountId;
$account = $accountRepository->findNull($accountId);
if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) {
$finalList->push($account);
}
}
if (0 === $finalList->count()) {
$this->error('Please make sure all accounts in --accounts are asset accounts or liabilities.');
return false;
}
$this->accounts = $finalList;
return true;
}
/**
* @return bool
*/
private function verifyInputRuleGroups(): bool
{
$ruleGroupString = $this->option('rule_groups');
if (null === $ruleGroupString || '' === $ruleGroupString) {
// can be empty.
return true;
}
$ruleGroupList = explode(',', $ruleGroupString);
if (0 === count($ruleGroupList)) {
// can be empty.
return true;
}
foreach ($ruleGroupList as $ruleGroupId) {
$ruleGroup = $this->ruleGroupRepository->find((int)$ruleGroupId);
if ($ruleGroup->active) {
$this->ruleGroupSelection[] = $ruleGroup->id;
}
if (false === $ruleGroup->active) {
$this->warn(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title));
}
}
return true;
}
/**
* @return bool
*/
private function verifyInputRules(): bool
{
$ruleString = $this->option('rules');
if (null === $ruleString || '' === $ruleString) {
// can be empty.
return true;
}
$ruleList = explode(',', $ruleString);
if (0 === count($ruleList)) {
// can be empty.
return true;
}
foreach ($ruleList as $ruleId) {
$rule = $this->ruleRepository->find((int)$ruleId);
if (null !== $rule && $rule->active) {
$this->ruleSelection[] = $rule->id;
}
}
return true;
}
/**
* @throws FireflyException
*/
private function verifyInputDates(): void
{
// parse start date.
$startDate = Carbon::now()->startOfMonth();
$startString = $this->option('start_date');
if (null === $startString) {
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$repository->setUser($this->getUser());
$first = $repository->firstNull();
if (null !== $first) {
$startDate = $first->date;
}
}
if (null !== $startString && '' !== $startString) {
$startDate = Carbon::createFromFormat('Y-m-d', $startString);
}
// parse end date
$endDate = Carbon::now();
$endString = $this->option('end_date');
if (null !== $endString && '' !== $endString) {
$endDate = Carbon::createFromFormat('Y-m-d', $endString);
}
if ($startDate > $endDate) {
[$endDate, $startDate] = [$startDate, $endDate];
}
$this->startDate = $startDate;
$this->endDate = $endDate;
}
/**
*/
private function grabAllRules(): void
{
$this->groups = $this->ruleGroupRepository->getActiveGroups();
}
/**
* @param Rule $rule
* @param RuleGroup $group
* @return bool
*/
private function includeRule(Rule $rule, RuleGroup $group): bool
{
return in_array($group->id, $this->ruleGroupSelection, true) ||
in_array($rule->id, $this->ruleSelection, true) ||
$this->allRules;
}
}

View File

@ -51,7 +51,7 @@ class StoredGroupEventHandler
foreach ($journals as $journal) {
$ruleGroupRepos->setUser($journal->user);
$groups = $ruleGroupRepos->getActiveGroups($journal->user);
$groups = $ruleGroupRepos->getActiveGroups();
/** @var RuleGroup $group */
foreach ($groups as $group) {

View File

@ -52,7 +52,7 @@ class UpdatedGroupEventHandler
foreach ($journals as $journal) {
$ruleGroupRepos->setUser($journal->user);
$groups = $ruleGroupRepos->getActiveGroups($journal->user);
$groups = $ruleGroupRepos->getActiveGroups();
/** @var RuleGroup $group */
foreach ($groups as $group) {

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Http\Controllers\Account;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
@ -76,8 +77,8 @@ class ShowController extends Controller
/**
* Show an account.
*
* @param Request $request
* @param Account $account
* @param Request $request
* @param Account $account
* @param Carbon|null $start
* @param Carbon|null $end
*
@ -119,19 +120,22 @@ class ShowController extends Controller
$subTitle = (string)trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$periods = $this->getAccountPeriodOverview($account, $end);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
$collector->setRange($start, $end);
$transactions = $collector->getPaginatedTransactions();
$transactions->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
$showAll = false;
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setLimit($pageSize)
->setPage($page)
->setRange($start, $end);
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
$showAll = false;
return view(
'accounts.show',
compact(
'account', 'showAll', 'what', 'currency', 'today', 'periods', 'subTitleIcon', 'transactions', 'subTitle', 'start', 'end',
'account', 'showAll', 'what', 'currency', 'today', 'periods', 'subTitleIcon', 'groups', 'subTitle', 'start', 'end',
'chartUri'
)
);

View File

@ -34,19 +34,18 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class RuleGroup.
*
* @property bool $active
* @property User $user
* @property Carbon $created_at
* @property Carbon $updated_at
* @property string $title
* @property string $text
* @property int $id
* @property int $order
* @property bool $active
* @property User $user
* @property Carbon $created_at
* @property Carbon $updated_at
* @property string $title
* @property string $text
* @property int $id
* @property int $order
* @property Collection $rules
* @property string description
* @property string description
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property int $user_id
* @property string|null $description
* @method static bool|null forceDelete()
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\RuleGroup newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\RuleGroup newQuery()
@ -64,6 +63,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\RuleGroup whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup withTrashed()
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup withoutTrashed()
* @property bool $stop_processing
* @mixin \Eloquent
*/
class RuleGroup extends Model
@ -76,15 +76,16 @@ class RuleGroup extends Model
*/
protected $casts
= [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'active' => 'boolean',
'order' => 'int',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'active' => 'boolean',
'stop_processing' => 'boolean',
'order' => 'int',
];
/** @var array Fields that can be filled */
protected $fillable = ['user_id', 'order', 'title', 'description', 'active'];
protected $fillable = ['user_id', 'stop_processing', 'order', 'title', 'description', 'active'];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).

View File

@ -56,7 +56,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
}
/**
* @param RuleGroup $ruleGroup
* @param RuleGroup $ruleGroup
* @param RuleGroup|null $moveTo
*
* @return bool
@ -85,6 +85,49 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return true;
}
/**
* @return bool
*/
public function resetRuleGroupOrder(): bool
{
$this->user->ruleGroups()->whereNotNull('deleted_at')->update(['order' => 0]);
$set = $this->user->ruleGroups()->where('active', 1)->orderBy('order', 'ASC')->get();
$count = 1;
/** @var RuleGroup $entry */
foreach ($set as $entry) {
$entry->order = $count;
$entry->save();
++$count;
}
return true;
}
/**
* @param RuleGroup $ruleGroup
*
* @return bool
*/
public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool
{
$ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]);
$set = $ruleGroup->rules()
->orderBy('order', 'ASC')
->orderBy('updated_at', 'DESC')
->get();
$count = 1;
/** @var Rule $entry */
foreach ($set as $entry) {
$entry->order = $count;
$entry->save();
++$count;
}
return true;
}
/**
* @param int $ruleGroupId
*
@ -109,13 +152,11 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
}
/**
* @param User $user
*
* @return Collection
*/
public function getActiveGroups(User $user): Collection
public function getActiveGroups(): Collection
{
return $user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(['rule_groups.*']);
return $this->user->ruleGroups()->where('rule_groups.active', 1)->orderBy('order', 'ASC')->get(['rule_groups.*']);
}
/**
@ -160,16 +201,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
->get(['rules.*']);
}
/**
* @return int
*/
public function getHighestOrderRuleGroup(): int
{
$entry = $this->user->ruleGroups()->max('order');
return (int)$entry;
}
/**
* @param User $user
*
@ -253,49 +284,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return true;
}
/**
* @return bool
*/
public function resetRuleGroupOrder(): bool
{
$this->user->ruleGroups()->whereNotNull('deleted_at')->update(['order' => 0]);
$set = $this->user->ruleGroups()->where('active', 1)->orderBy('order', 'ASC')->get();
$count = 1;
/** @var RuleGroup $entry */
foreach ($set as $entry) {
$entry->order = $count;
$entry->save();
++$count;
}
return true;
}
/**
* @param RuleGroup $ruleGroup
*
* @return bool
*/
public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool
{
$ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]);
$set = $ruleGroup->rules()
->orderBy('order', 'ASC')
->orderBy('updated_at', 'DESC')
->get();
$count = 1;
/** @var Rule $entry */
foreach ($set as $entry) {
$entry->order = $count;
$entry->save();
++$count;
}
return true;
}
/**
* @param User $user
*/
@ -328,9 +316,19 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return $newRuleGroup;
}
/**
* @return int
*/
public function getHighestOrderRuleGroup(): int
{
$entry = $this->user->ruleGroups()->max('order');
return (int)$entry;
}
/**
* @param RuleGroup $ruleGroup
* @param array $data
* @param array $data
*
* @return RuleGroup
*/

View File

@ -59,11 +59,9 @@ interface RuleGroupRepositoryInterface
public function get(): Collection;
/**
* @param User $user
*
* @return Collection
*/
public function getActiveGroups(User $user): Collection;
public function getActiveGroups(): Collection;
/**
* @param RuleGroup $group

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Support\Http\Controllers;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Collector\GroupSumCollectorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
@ -69,13 +70,12 @@ trait PeriodOverview
* The method has been refactored recently for better performance.
*
* @param Account $account The account involved
* @param Carbon $date The start date.
* @param Carbon $date The start date.
*
* @return Collection
*/
protected function getAccountPeriodOverview(Account $account, Carbon $date): Collection
{
throw new FireflyException('Is using collector.');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$range = app('preferences')->get('viewRange', '1M')->data;
@ -100,25 +100,30 @@ trait PeriodOverview
$entries = new Collection;
// loop dates
foreach ($dates as $currentDate) {
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($currentDate['start'], $currentDate['end'])->setTypes([TransactionType::DEPOSIT])
->withOpposingAccount();
$earnedSet = $collector->getTransactions();
$earned = $this->groupByCurrency($earnedSet);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($currentDate['start'], $currentDate['end'])->setTypes([TransactionType::WITHDRAWAL])
->withOpposingAccount();
$spentSet = $collector->getTransactions();
// collect from start to end:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]));
$collector->setRange($currentDate['start'], $currentDate['end']);
$collector->setTypes([TransactionType::DEPOSIT]);
$earnedSet = $collector->getExtractedJournals();
$earned = $this->groupByCurrency($earnedSet);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]));
$collector->setRange($currentDate['start'], $currentDate['end']);
$collector->setTypes([TransactionType::WITHDRAWAL]);
$spentSet = $collector->getExtractedJournals();
$spent = $this->groupByCurrency($spentSet);
$title = app('navigation')->periodShow($currentDate['start'], $currentDate['period']);
/** @noinspection PhpUndefinedMethodInspection */
$entries->push(
[
'transactions' => 0,
'transactions' => count($spentSet) + count($earnedSet),
'title' => $title,
'spent' => $spent,
'earned' => $earned,
@ -127,17 +132,44 @@ trait PeriodOverview
]
);
}
$cache->store($entries);
//$cache->store($entries);
return $entries;
}
/**
* @param array $journals
*
* @return array
*/
private function groupByCurrency(array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id'];
if (!isset($return[$currencyId])) {
$currency = new TransactionCurrency;
$currency->symbol = $journal['currency_symbol'];
$currency->decimal_places = $journal['currency_decimal_places'];
$currency->name = $journal['currency_name'];
$return[$currencyId] = [
'amount' => '0',
'currency' => $currency,
//'currency' => 'x',//$currency,
];
}
$return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $journal['amount']);
}
return $return;
}
/**
* Overview for single category. Has been refactored recently.
*
* @param Category $category
* @param Carbon $date
* @param Carbon $date
*
* @return Collection
*/
@ -357,7 +389,7 @@ trait PeriodOverview
/**
* This shows a period overview for a tag. It goes back in time and lists all relevant transactions and sums.
*
* @param Tag $tag
* @param Tag $tag
*
* @param Carbon $date
*
@ -524,31 +556,4 @@ trait PeriodOverview
return $return;
}
/**
* @param array $journals
*
* @return array
*/
private function groupByCurrency(array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id'];
if (!isset($return[$currencyId])) {
$currency = new TransactionCurrency;
$currency->symbol = $journal['currency_symbol'];
$currency->decimal_places = $journal['currency_decimal_places'];
$currency->name = $journal['currency_name'];
$return[$currencyId] = [
'amount' => '0',
'currency' => $currency,
];
}
$return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $journal['amount']);
}
return $return;
}
}

View File

@ -62,26 +62,6 @@ class Processor
$this->actions = new Collection;
}
/**
* Return found triggers
*
* @return int
*/
public function getFoundTriggers(): int
{
return $this->foundTriggers;
}
/**
* Set found triggers
*
* @param int $foundTriggers
*/
public function setFoundTriggers(int $foundTriggers): void
{
$this->foundTriggers = $foundTriggers;
}
/**
* Returns the rule
*
@ -127,6 +107,126 @@ class Processor
return false;
}
/**
* Method to check whether the current transaction would be triggered
* by the given list of triggers.
*
* @return bool
*/
private function triggered(): bool
{
Log::debug('start of Processor::triggered()');
$foundTriggers = $this->getFoundTriggers();
$hitTriggers = 0;
Log::debug(sprintf('Found triggers starts at %d', $foundTriggers));
/** @var AbstractTrigger $trigger */
foreach ($this->triggers as $trigger) {
++$foundTriggers;
Log::debug(sprintf('Now checking trigger %s with value %s', \get_class($trigger), $trigger->getTriggerValue()));
/** @var AbstractTrigger $trigger */
if ($trigger->triggered($this->journal)) {
Log::debug('Is a match!');
++$hitTriggers;
// is non-strict? then return true!
if (!$this->strict && UserAction::class !== \get_class($trigger)) {
Log::debug('Rule is set as non-strict, return true!');
return true;
}
if (!$this->strict && UserAction::class === \get_class($trigger)) {
Log::debug('Rule is set as non-strict, but action was "user-action". Will not return true.');
}
}
if ($trigger->stopProcessing) {
Log::debug('Stop processing this trigger and break.');
break;
}
}
$result = ($hitTriggers === $foundTriggers && $foundTriggers > 0);
Log::debug('Result of triggered()', ['hitTriggers' => $hitTriggers, 'foundTriggers' => $foundTriggers, 'result' => $result]);
return $result;
}
/**
* Return found triggers
*
* @return int
*/
public function getFoundTriggers(): int
{
return $this->foundTriggers;
}
/**
* Set found triggers
*
* @param int $foundTriggers
*/
public function setFoundTriggers(int $foundTriggers): void
{
$this->foundTriggers = $foundTriggers;
}
/**
* Run the actions
*
* @return void
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function actions(): void
{
/**
* @var int
* @var RuleAction $action
*/
foreach ($this->actions as $action) {
/** @var ActionInterface $actionClass */
$actionClass = ActionFactory::getAction($action);
Log::debug(sprintf('Fire action %s on journal #%d', \get_class($actionClass), $this->journal->id));
$actionClass->act($this->journal);
if ($action->stop_processing) {
Log::debug('Stop processing now and break.');
break;
}
}
}
/**
* This method will scan the given transaction journal and check if it matches the triggers found in the Processor
* If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal
* matches all of the triggers (regardless of whether the Processor could act on it).
*
* @param array $journal
*
* @return bool
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function handleJournalArray(array $journal): bool
{
Log::debug(sprintf('handleJournalArray for journal #%d (group #%d)', $journal['transaction_journal_id'], $journal['transaction_group_id']));
// grab the actual journal.
$this->journal = TransactionJournal::find($journal['transaction_journal_id']);
// get all triggers:
$triggered = $this->triggered();
if ($triggered) {
Log::debug('Rule is triggered, go to actions.');
if ($this->actions->count() > 0) {
Log::debug('Has more than zero actions.');
$this->actions();
}
if (0 === $this->actions->count()) {
Log::info('Rule has no actions!');
}
return true;
}
return false;
}
/**
* This method will scan the given transaction journal and check if it matches the triggers found in the Processor
* If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal
@ -236,69 +336,4 @@ class Processor
}
}
/**
* Run the actions
*
* @return void
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function actions(): void
{
/**
* @var int
* @var RuleAction $action
*/
foreach ($this->actions as $action) {
/** @var ActionInterface $actionClass */
$actionClass = ActionFactory::getAction($action);
Log::debug(sprintf('Fire action %s on journal #%d', \get_class($actionClass), $this->journal->id));
$actionClass->act($this->journal);
if ($action->stop_processing) {
Log::debug('Stop processing now and break.');
break;
}
}
}
/**
* Method to check whether the current transaction would be triggered
* by the given list of triggers.
*
* @return bool
*/
private function triggered(): bool
{
Log::debug('start of Processor::triggered()');
$foundTriggers = $this->getFoundTriggers();
$hitTriggers = 0;
Log::debug(sprintf('Found triggers starts at %d', $foundTriggers));
/** @var AbstractTrigger $trigger */
foreach ($this->triggers as $trigger) {
++$foundTriggers;
Log::debug(sprintf('Now checking trigger %s with value %s', \get_class($trigger), $trigger->getTriggerValue()));
/** @var AbstractTrigger $trigger */
if ($trigger->triggered($this->journal)) {
Log::debug('Is a match!');
++$hitTriggers;
// is non-strict? then return true!
if (!$this->strict && UserAction::class !== \get_class($trigger)) {
Log::debug('Rule is set as non-strict, return true!');
return true;
}
if (!$this->strict && UserAction::class === \get_class($trigger)) {
Log::debug('Rule is set as non-strict, but action was "user-action". Will not return true.');
}
}
if ($trigger->stopProcessing) {
Log::debug('Stop processing this trigger and break.');
break;
}
}
$result = ($hitTriggers === $foundTriggers && $foundTriggers > 0);
Log::debug('Result of triggered()', ['hitTriggers' => $hitTriggers, 'foundTriggers' => $foundTriggers, 'result' => $result]);
return $result;
}
}

View File

@ -26,6 +26,9 @@ class ChangesForV480 extends Migration
$table->dropColumn('transaction_group_id');
}
);
Schema::table('rule_groups', function (Blueprint $table) {
$table->dropColumn('stop_processing');
});
}
/**
@ -50,5 +53,8 @@ class ChangesForV480 extends Migration
$table->foreign('transaction_group_id')->references('id')->on('transaction_groups')->onDelete('cascade');
}
);
Schema::table('rule_groups', function (Blueprint $table) {
$table->boolean('stop_processing')->default(false);
});
}
}