stupidLaravel(); if (!$this->verifyAccessToken()) { $this->friendlyError('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'); // always get all the rules of the user. $this->grabAllRules(); // loop all groups and rules and indicate if they're included: $rulesToApply = $this->getRulesToApply(); $count = $rulesToApply->count(); if (0 === $count) { $this->friendlyError('No rules or rule groups have been included.'); $this->friendlyWarning('Make a selection using:'); $this->friendlyWarning(' --rules=1,2,...'); $this->friendlyWarning(' --rule_groups=1,2,...'); $this->friendlyWarning(' --all_rules'); return 1; } // create new rule engine: /** @var RuleEngineInterface $ruleEngine */ $ruleEngine = app(RuleEngineInterface::class); $ruleEngine->setRules($rulesToApply); $ruleEngine->setUser($this->getUser()); // add the accounts as filter: $filterAccountList = []; foreach ($this->accounts as $account) { $filterAccountList[] = $account->id; } $list = implode(',', $filterAccountList); $ruleEngine->addOperator(['type' => 'account_id', 'value' => $list]); // add the date as a filter: $ruleEngine->addOperator(['type' => 'date_after', 'value' => $this->startDate->format('Y-m-d')]); $ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]); // start running rules. $this->friendlyLine(sprintf('Will apply %d rule(s) to your transaction(s).', $count)); // fire the rule(s) $ruleEngine->fire(); $this->friendlyLine(''); $end = round(microtime(true) - $start, 2); $this->friendlyPositive(sprintf('Done in %s seconds!', $end)); return 0; } /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should * be called from the handle method instead of using the constructor to initialize the command. */ private function stupidLaravel(): void { $this->allRules = false; $this->accounts = new Collection(); $this->ruleSelection = []; $this->ruleGroupSelection = []; $this->ruleRepository = app(RuleRepositoryInterface::class); $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); $this->acceptedAccounts = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value]; $this->groups = new Collection(); } /** * @throws FireflyException */ private function verifyInput(): bool { // verify account. $result = $this->verifyInputAccounts(); if (false === $result) { return false; } // verify rule groups. $this->verifyInputRuleGroups(); // verify rules. $this->verifyInputRules(); $this->verifyInputDates(); return true; } /** * @throws FireflyException */ private function verifyInputAccounts(): bool { $accountString = $this->option('accounts'); if (null === $accountString || '' === $accountString) { $this->friendlyError('Please use the --accounts option to indicate the accounts to apply rules to.'); return false; } $finalList = new Collection(); $accountList = explode(',', $accountString); /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); $accountRepository->setUser($this->getUser()); foreach ($accountList as $accountId) { $accountId = (int) $accountId; $account = $accountRepository->find($accountId); if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) { $finalList->push($account); } } if (0 === $finalList->count()) { $this->friendlyError('Please make sure all accounts in --accounts are asset accounts or liabilities.'); return false; } $this->accounts = $finalList; return true; } private function verifyInputRuleGroups(): bool { $ruleGroupString = $this->option('rule_groups'); if (null === $ruleGroupString || '' === $ruleGroupString) { // can be empty. return true; } $ruleGroupList = explode(',', $ruleGroupString); foreach ($ruleGroupList as $ruleGroupId) { $ruleGroup = $this->ruleGroupRepository->find((int) $ruleGroupId); if (true === $ruleGroup->active) { $this->ruleGroupSelection[] = $ruleGroup->id; } if (false === $ruleGroup->active) { $this->friendlyWarning(sprintf('Will ignore inactive rule group #%d ("%s")', $ruleGroup->id, $ruleGroup->title)); } } return true; } private function verifyInputRules(): bool { $ruleString = $this->option('rules'); if (null === $ruleString || '' === $ruleString) { // can be empty. return true; } $ruleList = explode(',', $ruleString); foreach ($ruleList as $ruleId) { $rule = $this->ruleRepository->find((int) $ruleId); if (null !== $rule && true === $rule->active) { $this->ruleSelection[] = $rule->id; } } return true; } /** * @throws FireflyException */ private function verifyInputDates(): void { // parse start date. $inputStart = today(config('app.timezone'))->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) { $inputStart = $first->date; } } if (null !== $startString && '' !== $startString) { $inputStart = Carbon::createFromFormat('Y-m-d', $startString); } // parse end date $inputEnd = today(config('app.timezone')); $endString = $this->option('end_date'); if (null !== $endString && '' !== $endString) { $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); } if (null === $inputEnd || null === $inputStart) { Log::error('Could not parse start or end date in verifyInputDate().'); return; } if ($inputStart > $inputEnd) { [$inputEnd, $inputStart] = [$inputStart, $inputEnd]; } $this->startDate = $inputStart; $this->endDate = $inputEnd; } private function grabAllRules(): void { $this->groups = $this->ruleGroupRepository->getActiveGroups(); } private function getRulesToApply(): Collection { $rulesToApply = new Collection(); /** @var RuleGroup $group */ foreach ($this->groups as $group) { $rules = $this->ruleGroupRepository->getActiveStoreRules($group); /** @var Rule $rule */ foreach ($rules as $rule) { // if in rule selection, or group in selection or all rules, it's included. $test = $this->includeRule($rule, $group); if (true === $test) { app('log')->debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); $rulesToApply->push($rule); } } } return $rulesToApply; } 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; } }