mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Replace transaction collector.
This commit is contained in:
parent
627ef09f11
commit
d13317095f
381
app/Console/Commands/Tools/ApplyRules.php
Normal file
381
app/Console/Commands/Tools/ApplyRules.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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'
|
||||
)
|
||||
);
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user