GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 4d9763...76aa8a )
by James
23:28 queued 10:43
created

app/Console/Commands/ApplyRules.php (2 issues)

1
<?php
2
3
/**
4
 * ApplyRules.php
5
 * Copyright (c) 2018 [email protected]
6
 *
7
 * This file is part of Firefly III.
8
 *
9
 * Firefly III is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * Firefly III is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
declare(strict_types=1);
24
25
namespace FireflyIII\Console\Commands;
26
27
use Carbon\Carbon;
28
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
29
use FireflyIII\Models\AccountType;
30
use FireflyIII\Models\Rule;
31
use FireflyIII\Models\RuleGroup;
32
use FireflyIII\Models\Transaction;
33
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
34
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
35
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
36
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
37
use FireflyIII\TransactionRules\Processor;
38
use Illuminate\Console\Command;
39
use Illuminate\Support\Collection;
40
41
/**
42
 *
43
 * Class ApplyRules
44
 *
45
 * @codeCoverageIgnore
46
 */
47
class ApplyRules extends Command
48
{
49
    use VerifiesAccessToken;
50
51
    /**
52
     * The console command description.
53
     *
54
     * @var string
55
     */
56
    protected $description = 'This command will apply your rules and rule groups on a selection of your transactions.';
57
    /**
58
     * The name and signature of the console command.
59
     *
60
     * @var string
61
     */
62
    protected $signature
63
        = 'firefly:apply-rules
64
                            {--user=1 : The user ID that the import should import for.}
65
                            {--token= : The user\'s access token.}
66
                            {--accounts= : A comma-separated list of asset accounts or liabilities to apply your rules to.}
67
                            {--rule_groups= : A comma-separated list of rule groups to apply. Take the ID\'s of these rule groups from the Firefly III interface.}
68
                            {--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.}
69
                            {--all_rules : If set, will overrule both settings and simply apply ALL of your rules.}
70
                            {--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}
71
                            {--end_date= : The date of the latest transaction to be included (inclusive). If omitted, will be your latest transaction ever. Format: YYYY-MM-DD}';
72
    /** @var Collection */
73
    private $accounts;
74
    /** @var Carbon */
75
    private $endDate;
76
    /** @var Collection */
77
    private $results;
78
    /** @var Collection */
79
    private $ruleGroups;
80
    /** @var Collection */
81
    private $rules;
82
    /** @var Carbon */
83
    private $startDate;
84
85
    /**
86
     * Create a new command instance.
87
     *
88
     * @return void
89
     */
90
    public function __construct()
91
    {
92
        parent::__construct();
93
        $this->accounts   = new Collection;
94
        $this->rules      = new Collection;
95
        $this->ruleGroups = new Collection;
96
        $this->results    = new Collection;
97
    }
98
99
    /**
100
     * Execute the console command.
101
     *
102
     * @return int
103
     * @throws \FireflyIII\Exceptions\FireflyException
104
     */
105
    public function handle(): int
106
    {
107
        if (!$this->verifyAccessToken()) {
108
            $this->error('Invalid access token.');
109
110
            return 1;
111
        }
112
113
        $result = $this->verifyInput();
114
        if (false === $result) {
115
            return 1;
116
        }
117
118
119
        // get transactions from asset accounts.
120
        /** @var TransactionCollectorInterface $collector */
121
        $collector = app(TransactionCollectorInterface::class);
122
        $collector->setUser($this->getUser());
123
        $collector->setAccounts($this->accounts);
124
        $collector->setRange($this->startDate, $this->endDate);
125
        $transactions = $collector->getTransactions();
126
        $count        = $transactions->count();
127
128
        // first run all rule groups:
129
        /** @var RuleGroupRepositoryInterface $ruleGroupRepos */
130
        $ruleGroupRepos = app(RuleGroupRepositoryInterface::class);
131
        $ruleGroupRepos->setUser($this->getUser());
132
133
        /** @var RuleGroup $ruleGroup */
134
        foreach ($this->ruleGroups as $ruleGroup) {
135
            $this->line(sprintf('Going to apply rule group "%s" to %d transaction(s).', $ruleGroup->title, $count));
136
            $rules = $ruleGroupRepos->getActiveStoreRules($ruleGroup);
137
            $this->applyRuleSelection($rules, $transactions, true);
138
        }
139
140
        // then run all rules (rule groups should be empty).
141
        if ($this->rules->count() > 0) {
142
143
            $this->line(sprintf('Will apply %d rule(s) to %d transaction(s)', $this->rules->count(), $transactions->count()));
144
            $this->applyRuleSelection($this->rules, $transactions, false);
145
        }
146
147
        // filter results:
148
        $this->results = $this->results->unique(
149
            function (Transaction $transaction) {
150
                return (int)$transaction->journal_id;
151
            }
152
        );
153
154
        $this->line('');
155
        if (0 === $this->results->count()) {
156
            $this->line('The rules were fired but did not influence any transactions.');
157
        }
158
        if ($this->results->count() > 0) {
159
            $this->line(sprintf('The rule(s) was/were fired, and influenced %d transaction(s).', $this->results->count()));
160
            foreach ($this->results as $result) {
161
                $this->line(
162
                    vsprintf(
163
                        'Transaction #%d: "%s" (%s %s)',
164
                        [
165
                            $result->journal_id,
166
                            $result->description,
167
                            $result->transaction_currency_code,
168
                            round($result->transaction_amount, $result->transaction_currency_dp),
169
                        ]
170
                    )
171
                );
172
            }
173
        }
174
175
        return 0;
176
    }
177
178
    /**
179
     * @param Collection $rules
180
     * @param Collection $transactions
181
     * @param bool       $breakProcessing
182
     *
183
     * @throws \FireflyIII\Exceptions\FireflyException
184
     */
185
    private function applyRuleSelection(Collection $rules, Collection $transactions, bool $breakProcessing): void
186
    {
187
        $bar = $this->output->createProgressBar($rules->count() * $transactions->count());
188
189
        /** @var Rule $rule */
190
        foreach ($rules as $rule) {
191
            /** @var Processor $processor */
192
            $processor = app(Processor::class);
193
            $processor->make($rule, true);
194
195
            /** @var Transaction $transaction */
196
            foreach ($transactions as $transaction) {
197
                /** @noinspection DisconnectedForeachInstructionInspection */
198
                $bar->advance();
199
                $result = $processor->handleTransaction($transaction);
200
                if (true === $result) {
201
                    $this->results->push($transaction);
202
                }
203
            }
204
            if (true === $rule->stop_processing && true === $breakProcessing) {
205
                $this->line('');
206
                $this->line(sprintf('Rule #%d ("%s") says to stop processing.', $rule->id, $rule->title));
207
208
                return;
209
            }
210
        }
211
        $this->line('');
212
    }
213
214
    /**
215
     *
216
     * @throws \FireflyIII\Exceptions\FireflyException
217
     */
218
    private function grabAllRules(): void
219
    {
220
        if (true === $this->option('all_rules')) {
221
            /** @var RuleRepositoryInterface $ruleRepos */
222
            $ruleRepos = app(RuleRepositoryInterface::class);
223
            $ruleRepos->setUser($this->getUser());
224
            $this->rules = $ruleRepos->getAll();
225
226
            // reset rule groups.
227
            $this->ruleGroups = new Collection;
228
        }
229
    }
230
231
    /**
232
     *
233
     * @throws \FireflyIII\Exceptions\FireflyException
234
     */
235
    private function parseDates(): void
236
    {
237
        // parse start date.
238
        $startDate   = Carbon::now()->startOfMonth();
239
        $startString = $this->option('start_date');
240
        if (null === $startString) {
241
            /** @var JournalRepositoryInterface $repository */
242
            $repository = app(JournalRepositoryInterface::class);
243
            $repository->setUser($this->getUser());
244
            $first = $repository->firstNull();
245
            if (null !== $first) {
246
                $startDate = $first->date;
247
            }
248
        }
249
        if (null !== $startString && '' !== $startString) {
250
            $startDate = Carbon::createFromFormat('Y-m-d', $startString);
251
        }
252
253
        // parse end date
254
        $endDate   = Carbon::now();
255
        $endString = $this->option('end_date');
256
        if (null !== $endString && '' !== $endString) {
257
            $endDate = Carbon::createFromFormat('Y-m-d', $endString);
258
        }
259
260
        if ($startDate > $endDate) {
261
            [$endDate, $startDate] = [$startDate, $endDate];
262
        }
263
264
        $this->startDate = $startDate;
0 ignored issues
show
Documentation Bug introduced by
It seems like $startDate can also be of type false. However, the property $startDate is declared as type Carbon\Carbon. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
265
        $this->endDate   = $endDate;
0 ignored issues
show
Documentation Bug introduced by
It seems like $endDate can also be of type false. However, the property $endDate is declared as type Carbon\Carbon. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
266
    }
267
268
    /**
269
     * @return bool
270
     * @throws \FireflyIII\Exceptions\FireflyException
271
     */
272
    private function verifyInput(): bool
273
    {
274
        // verify account.
275
        $result = $this->verifyInputAccounts();
276
        if (false === $result) {
277
            return $result;
278
        }
279
280
        // verify rule groups.
281
        $result = $this->verifyRuleGroups();
282
        if (false === $result) {
283
            return $result;
284
        }
285
286
        // verify rules.
287
        $result = $this->verifyRules();
288
        if (false === $result) {
289
            return $result;
290
        }
291
292
        $this->grabAllRules();
293
        $this->parseDates();
294
295
        //$this->line('Number of rules found: ' . $this->rules->count());
296
        $this->line('Start date is ' . $this->startDate->format('Y-m-d'));
297
        $this->line('End date is ' . $this->endDate->format('Y-m-d'));
298
299
        return true;
300
    }
301
302
    /**
303
     * @return bool
304
     * @throws \FireflyIII\Exceptions\FireflyException
305
     */
306
    private function verifyInputAccounts(): bool
307
    {
308
        $accountString = $this->option('accounts');
309
        if (null === $accountString || '' === $accountString) {
310
            $this->error('Please use the --accounts to indicate the accounts to apply rules to.');
311
312
            return false;
313
        }
314
        $finalList   = new Collection;
315
        $accountList = explode(',', $accountString);
316
317
        if (0 === \count($accountList)) {
318
            $this->error('Please use the --accounts to indicate the accounts to apply rules to.');
319
320
            return false;
321
        }
322
323
        /** @var AccountRepositoryInterface $accountRepository */
324
        $accountRepository = app(AccountRepositoryInterface::class);
325
        $accountRepository->setUser($this->getUser());
326
327
        foreach ($accountList as $accountId) {
328
            $accountId = (int)$accountId;
329
            $account   = $accountRepository->findNull($accountId);
330
            if (null !== $account
331
                && \in_array(
332
                    $account->accountType->type, [AccountType::DEFAULT, AccountType::DEBT, AccountType::ASSET, AccountType::LOAN, AccountType::MORTGAGE], true
333
                )) {
334
                $finalList->push($account);
335
            }
336
        }
337
338
        if (0 === $finalList->count()) {
339
            $this->error('Please make sure all accounts in --accounts are asset accounts or liabilities.');
340
341
            return false;
342
        }
343
        $this->accounts = $finalList;
344
345
        return true;
346
347
    }
348
349
    /**
350
     * @return bool
351
     * @throws \FireflyIII\Exceptions\FireflyException
352
     */
353
    private function verifyRuleGroups(): bool
354
    {
355
        $ruleGroupString = $this->option('rule_groups');
356
        if (null === $ruleGroupString || '' === $ruleGroupString) {
357
            // can be empty.
358
            return true;
359
        }
360
        $ruleGroupList = explode(',', $ruleGroupString);
361
362
        if (0 === \count($ruleGroupList)) {
363
            // can be empty.
364
365
            return true;
366
        }
367
        /** @var RuleGroupRepositoryInterface $ruleGroupRepos */
368
        $ruleGroupRepos = app(RuleGroupRepositoryInterface::class);
369
        $ruleGroupRepos->setUser($this->getUser());
370
371
        foreach ($ruleGroupList as $ruleGroupId) {
372
            $ruleGroupId = (int)$ruleGroupId;
373
            $ruleGroup   = $ruleGroupRepos->find($ruleGroupId);
374
            $this->ruleGroups->push($ruleGroup);
375
        }
376
377
        return true;
378
    }
379
380
    /**
381
     * @return bool
382
     * @throws \FireflyIII\Exceptions\FireflyException
383
     */
384
    private function verifyRules(): bool
385
    {
386
        $ruleString = $this->option('rules');
387
        if (null === $ruleString || '' === $ruleString) {
388
            // can be empty.
389
            return true;
390
        }
391
        $finalList = new Collection;
392
        $ruleList  = explode(',', $ruleString);
393
394
        if (0 === \count($ruleList)) {
395
            // can be empty.
396
397
            return true;
398
        }
399
        /** @var RuleRepositoryInterface $ruleRepos */
400
        $ruleRepos = app(RuleRepositoryInterface::class);
401
        $ruleRepos->setUser($this->getUser());
402
403
        foreach ($ruleList as $ruleId) {
404
            $ruleId = (int)$ruleId;
405
            $rule   = $ruleRepos->find($ruleId);
406
            if (null !== $rule) {
407
                $finalList->push($rule);
408
            }
409
        }
410
        if ($finalList->count() > 0) {
411
            // reset rule groups.
412
            $this->ruleGroups = new Collection;
413
            $this->rules      = $finalList;
414
        }
415
416
        return true;
417
    }
418
419
420
}
421