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.

Issues (724)

app/Repositories/Tag/TagRepository.php (6 issues)

1
<?php
2
/**
3
 * TagRepository.php
4
 * Copyright (c) 2019 [email protected]
5
 *
6
 * This file is part of Firefly III (https://github.com/firefly-iii).
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License as
10
 * published by the Free Software Foundation, either version 3 of the
11
 * License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace FireflyIII\Repositories\Tag;
24
25
use Carbon\Carbon;
26
use DB;
27
use FireflyIII\Factory\TagFactory;
28
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
29
use FireflyIII\Models\Location;
30
use FireflyIII\Models\RuleAction;
31
use FireflyIII\Models\RuleTrigger;
32
use FireflyIII\Models\Tag;
33
use FireflyIII\Models\TransactionType;
34
use FireflyIII\User;
35
use Illuminate\Support\Collection;
36
use Log;
37
38
/**
39
 * Class TagRepository.
40
 *
41
 */
42
class TagRepository implements TagRepositoryInterface
43
{
44
    /** @var User */
45
    private $user;
46
47
    /**
48
     * Constructor.
49
     */
50
    public function __construct()
51
    {
52
        if ('testing' === config('app.env')) {
53
            Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
54
        }
55
    }
56
57
    /**
58
     * @return int
59
     */
60
    public function count(): int
61
    {
62
        return $this->user->tags()->count();
63
    }
64
65
    /**
66
     * @param Tag $tag
67
     *
68
     * @return bool
69
     * @throws \Exception
70
     */
71
    public function destroy(Tag $tag): bool
72
    {
73
        $tag->transactionJournals()->sync([]);
74
        $tag->delete();
75
76
        return true;
77
    }
78
79
    /**
80
     * Destroy all tags.
81
     */
82
    public function destroyAll(): void
83
    {
84
        $tags = $this->get();
85
        /** @var Tag $tag */
86
        foreach ($tags as $tag) {
87
            DB::table('tag_transaction_journal')->where('tag_id', $tag->id)->delete();
88
            $tag->delete();
89
        }
90
    }
91
92
    /**
93
     * @param Tag    $tag
94
     * @param Carbon $start
95
     * @param Carbon $end
96
     *
97
     * @return string
98
     */
99
    public function earnedInPeriod(Tag $tag, Carbon $start, Carbon $end): string
100
    {
101
        /** @var GroupCollectorInterface $collector */
102
        $collector = app(GroupCollectorInterface::class);
103
104
        $collector->setUser($this->user);
105
        $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setTag($tag);
106
107
        return $collector->getSum();
108
    }
109
110
    /**
111
     * @param Tag    $tag
112
     * @param Carbon $start
113
     * @param Carbon $end
114
     *
115
     * @return array
116
     */
117
    public function expenseInPeriod(Tag $tag, Carbon $start, Carbon $end): array
118
    {
119
        /** @var GroupCollectorInterface $collector */
120
        $collector = app(GroupCollectorInterface::class);
121
122
        $collector->setUser($this->user);
123
        $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setTag($tag);
124
125
        return $collector->getExtractedJournals();
126
    }
127
128
    /**
129
     * @param string $tag
130
     *
131
     * @return Tag|null
132
     */
133
    public function findByTag(string $tag): ?Tag
134
    {
135
        return $this->user->tags()->where('tag', $tag)->first();
136
    }
137
138
    /**
139
     * @param int $tagId
140
     *
141
     * @return Tag|null
142
     */
143
    public function findNull(int $tagId): ?Tag
144
    {
145
        return $this->user->tags()->find($tagId);
146
    }
147
148
    /**
149
     * @param Tag $tag
150
     *
151
     * @return Carbon|null
152
     */
153
    public function firstUseDate(Tag $tag): ?Carbon
154
    {
155
        $journal = $tag->transactionJournals()->orderBy('date', 'ASC')->first();
156
        if (null !== $journal) {
157
            return $journal->date;
158
        }
159
160
        return null;
161
    }
162
163
    /**
164
     * @return Collection
165
     */
166
    public function get(): Collection
167
    {
168
        /** @var Collection $tags */
169
        $tags = $this->user->tags()->orderBy('tag', 'ASC')->get();
170
171
        return $tags;
172
    }
173
174
    /**
175
     * @inheritDoc
176
     */
177
    public function getLocation(Tag $tag): ?Location
178
    {
179
        return $tag->locations()->first();
180
    }
181
182
    /**
183
     * @param int|null $year
184
     *
185
     * @return Collection
186
     */
187
    public function getTagsInYear(?int $year): array
188
    {
189
        // get all tags in the year (if present):
190
        $tagQuery = $this->user->tags()->with(['locations', 'attachments'])->orderBy('tags.tag');
191
192
        // add date range (or not):
193
        if (null === $year) {
194
            Log::debug('Get tags without a date.');
195
            $tagQuery->whereNull('tags.date');
196
        }
197
198
        if (null !== $year) {
199
            Log::debug(sprintf('Get tags with year %s.', $year));
200
            $tagQuery->where('tags.date', '>=', $year . '-01-01 00:00:00')->where('tags.date', '<=', $year . '-12-31 23:59:59');
201
        }
202
        $collection = $tagQuery->get();
203
        $return     = [];
204
        /** @var Tag $tag */
205
        foreach ($collection as $tag) {
206
            // return value for tag cloud:
207
            $return[$tag->id] = [
208
                'tag'         => $tag->tag,
209
                'id'          => $tag->id,
210
                'created_at'  => $tag->created_at,
211
                'location'    => $tag->locations->first(),
212
                'attachments' => $tag->attachments,
213
            ];
214
        }
215
216
        return $return;
217
    }
218
219
    /**
220
     * @param Tag    $tag
221
     * @param Carbon $start
222
     * @param Carbon $end
223
     *
224
     * @return array
225
     */
226
    public function incomeInPeriod(Tag $tag, Carbon $start, Carbon $end): array
227
    {
228
        /** @var GroupCollectorInterface $collector */
229
        $collector = app(GroupCollectorInterface::class);
230
231
        $collector->setUser($this->user);
232
        $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setTag($tag);
233
234
        return $collector->getExtractedJournals();
235
    }
236
237
    /**
238
     * @param Tag $tag
239
     *
240
     * @return Carbon|null
241
     */
242
    public function lastUseDate(Tag $tag): ?Carbon
243
    {
244
        $journal = $tag->transactionJournals()->orderBy('date', 'DESC')->first();
245
        if (null !== $journal) {
246
            return $journal->date;
247
        }
248
249
        return null;
250
    }
251
252
    /**
253
     * Will return the newest tag (if known) or NULL.
254
     *
255
     * @return Tag|null
256
     */
257
    public function newestTag(): ?Tag
258
    {
259
        return $this->user->tags()->whereNotNull('date')->orderBy('date', 'DESC')->first();
260
    }
261
262
    /**
263
     * @return Tag
264
     */
265
    public function oldestTag(): ?Tag
266
    {
267
        return $this->user->tags()->whereNotNull('date')->orderBy('date', 'ASC')->first();
268
    }
269
270
    /**
271
     * Find one or more tags based on the query.
272
     *
273
     * @param string $query
274
     *
275
     * @return Collection
276
     */
277
    public function searchTag(string $query): Collection
278
    {
279
        $search = sprintf('%%%s%%', $query);
280
281
        return $this->user->tags()->where('tag', 'LIKE', $search)->get(['tags.*']);
282
    }
283
284
    /**
285
     * Search the users tags.
286
     *
287
     * @param string $query
288
     *
289
     * @return Collection
290
     */
291
    public function searchTags(string $query): Collection
292
    {
293
        /** @var Collection $tags */
294
        $tags = $this->user->tags()->orderBy('tag', 'ASC');
295
        if ('' !== $query) {
296
            $search = sprintf('%%%s%%', $query);
297
            $tags->where('tag', 'LIKE', $search);
298
        }
299
300
        return $tags->get();
0 ignored issues
show
The call to Illuminate\Support\Collection::get() has too few arguments starting with key. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

300
        return $tags->/** @scrutinizer ignore-call */ get();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
301
    }
302
303
    /**
304
     * @param User $user
305
     */
306
    public function setUser(User $user): void
307
    {
308
        $this->user = $user;
309
    }
310
311
    /**
312
     * @param Tag    $tag
313
     * @param Carbon $start
314
     * @param Carbon $end
315
     *
316
     * @return string
317
     */
318
    public function spentInPeriod(Tag $tag, Carbon $start, Carbon $end): string
319
    {
320
        /** @var GroupCollectorInterface $collector */
321
        $collector = app(GroupCollectorInterface::class);
322
323
        $collector->setUser($this->user);
324
        $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setTag($tag);
325
326
        return $collector->getSum();
327
    }
328
329
    /**
330
     * @param array $data
331
     *
332
     * @return Tag
333
     */
334
    public function store(array $data): Tag
335
    {
336
        /** @var TagFactory $factory */
337
        $factory = app(TagFactory::class);
338
        $factory->setUser($this->user);
339
340
        return $factory->create($data);
341
    }
342
343
    /**
344
     * @param Tag         $tag
345
     * @param Carbon|null $start
346
     * @param Carbon|null $end
347
     *
348
     * @return array
349
     *
350
     */
351
    public function sumsOfTag(Tag $tag, ?Carbon $start, ?Carbon $end): array
352
    {
353
        /** @var GroupCollectorInterface $collector */
354
        $collector = app(GroupCollectorInterface::class);
355
356
        if (null !== $start && null !== $end) {
357
            $collector->setRange($start, $end);
358
        }
359
360
        $collector->setTag($tag)->withAccountInformation();
361
        $journals = $collector->getExtractedJournals();
362
363
        $sums = [
364
            TransactionType::WITHDRAWAL      => '0',
365
            TransactionType::DEPOSIT         => '0',
366
            TransactionType::TRANSFER        => '0',
367
            TransactionType::RECONCILIATION  => '0',
368
            TransactionType::OPENING_BALANCE => '0',
369
        ];
370
371
        /** @var array $journal */
372
        foreach ($journals as $journal) {
373
            $amount = app('steam')->positive((string)$journal['amount']);
374
            $type   = $journal['transaction_type_type'];
375
            if (TransactionType::WITHDRAWAL === $type) {
376
                $amount = bcmul($amount, '-1');
377
            }
378
            $sums[$type] = bcadd($sums[$type], $amount);
379
        }
380
381
        return $sums;
382
    }
383
384
    /**
385
     * Generates a tag cloud.
386
     *
387
     * @param int|null $year
388
     *
389
     * @return array
390
     * @deprecated
391
     */
392
    public function tagCloud(?int $year): array
393
    {
394
        // Some vars
395
        $tags = $this->getTagsInYear($year);
396
397
        $max           = $this->getMaxAmount($tags);
0 ignored issues
show
$tags of type array is incompatible with the type Illuminate\Support\Collection expected by parameter $tags of FireflyIII\Repositories\...ository::getMaxAmount(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

397
        $max           = $this->getMaxAmount(/** @scrutinizer ignore-type */ $tags);
Loading history...
398
        $min           = $this->getMinAmount($tags);
0 ignored issues
show
$tags of type array is incompatible with the type Illuminate\Support\Collection expected by parameter $tags of FireflyIII\Repositories\...ository::getMinAmount(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

398
        $min           = $this->getMinAmount(/** @scrutinizer ignore-type */ $tags);
Loading history...
399
        $diff          = bcsub($max, $min);
400
        $return        = [];
401
        $minimumFont   = '12'; // default scale is from 12 to 24, so 12 points.
402
        $maxPoints     = '12';
403
        $pointsPerCoin = '0';
404
405
        Log::debug(sprintf('Minimum is %s, maximum is %s, difference is %s', $min, $max, $diff));
406
407
        if (0 !== bccomp($diff, '0')) { // for each full coin in tag, add so many points
408
            // minus the smallest tag.
409
            $pointsPerCoin = bcdiv($maxPoints, $diff);
410
        }
411
412
        Log::debug(sprintf('Each coin in a tag earns it %s points', $pointsPerCoin));
413
        /** @var Tag $tag */
414
        foreach ($tags as $tag) {
415
            $amount       = (string)$tag->amount_sum;
416
            $amount       = '' === $amount ? '0' : $amount;
417
            $amountMin    = bcsub($amount, $min);
418
            $pointsForTag = bcmul($amountMin, $pointsPerCoin);
419
            $fontSize     = bcadd($minimumFont, $pointsForTag);
420
            Log::debug(sprintf('Tag "%s": Amount is %s, so points is %s', $tag->tag, $amount, $fontSize));
421
422
            // return value for tag cloud:
423
            $return[$tag->id] = [
424
                'size'       => $fontSize,
425
                'tag'        => $tag->tag,
426
                'id'         => $tag->id,
427
                'created_at' => $tag->created_at,
428
                'location'   => $this->getLocation($tag),
429
            ];
430
        }
431
432
        return $return;
433
    }
434
435
    /**
436
     * @param Tag    $tag
437
     * @param Carbon $start
438
     * @param Carbon $end
439
     *
440
     * @return array
441
     */
442
    public function transferredInPeriod(Tag $tag, Carbon $start, Carbon $end): array
443
    {
444
        /** @var GroupCollectorInterface $collector */
445
        $collector = app(GroupCollectorInterface::class);
446
        $collector->setUser($this->user);
447
        $collector->setRange($start, $end)->setTypes([TransactionType::TRANSFER])->setTag($tag);
448
449
        return $collector->getExtractedJournals();
450
    }
451
452
    /**
453
     * @param Tag   $tag
454
     * @param array $data
455
     *
456
     * @return Tag
457
     */
458
    public function update(Tag $tag, array $data): Tag
459
    {
460
        $oldTag           = $data['tag'];
0 ignored issues
show
The assignment to $oldTag is dead and can be removed.
Loading history...
461
        $tag->tag         = $data['tag'];
462
        $tag->date        = $data['date'];
463
        $tag->description = $data['description'];
464
        $tag->latitude    = null;
465
        $tag->longitude   = null;
466
        $tag->zoomLevel   = null;
467
        $tag->save();
468
469
        // update, delete or create location:
470
        $updateLocation = $data['update_location'] ?? false;
471
472
        // location must be updated?
473
        if (true === $updateLocation) {
474
            // if all set to NULL, delete
475
            if (null === $data['latitude'] && null === $data['longitude'] && null === $data['zoom_level']) {
476
                $tag->locations()->delete();
477
            }
478
479
            // otherwise, update or create.
480
            if (!(null === $data['latitude'] && null === $data['longitude'] && null === $data['zoom_level'])) {
481
                $location = $this->getLocation($tag);
482
                if (null === $location) {
483
                    $location = new Location;
484
                    $location->locatable()->associate($tag);
485
                }
486
487
                $location->latitude   = $data['latitude'] ?? config('firefly.default_location.latitude');
488
                $location->longitude  = $data['longitude'] ?? config('firefly.default_location.longitude');
489
                $location->zoom_level = $data['zoom_level'] ?? config('firefly.default_location.zoom_level');
490
                $location->save();
491
            }
492
        }
493
494
        return $tag;
495
    }
496
497
    /**
498
     * @param Collection $tags
499
     *
500
     * @return string
501
     */
502
    private function getMaxAmount(Collection $tags): string
503
    {
504
        $max = '0';
505
        /** @var Tag $tag */
506
        foreach ($tags as $tag) {
507
            $amount = (string)$tag->amount_sum;
508
            $amount = '' === $amount ? '0' : $amount;
509
            $max    = 1 === bccomp($amount, $max) ? $amount : $max;
510
511
        }
512
        Log::debug(sprintf('Maximum is %s.', $max));
513
514
        return $max;
515
    }
516
517
    /**
518
     * @param Collection $tags
519
     *
520
     * @return string
521
     *
522
     */
523
    private function getMinAmount(Collection $tags): string
524
    {
525
        $min = null;
526
527
        /** @var Tag $tag */
528
        foreach ($tags as $tag) {
529
            $amount = (string)$tag->amount_sum;
530
            $amount = '' === $amount ? '0' : $amount;
531
532
            if (null === $min) {
533
                $min = $amount;
534
            }
535
            $min = -1 === bccomp($amount, $min) ? $amount : $min;
536
        }
537
538
539
        if (null === $min) {
540
            $min = '0';
541
        }
542
        Log::debug(sprintf('Minimum is %s.', $min));
543
544
        return $min;
545
    }
546
547
    /**
548
     * @inheritDoc
549
     */
550
    public function getAttachments(Tag $tag): Collection
551
    {
552
        return $tag->attachments()->get();
553
    }
554
555
    /**
556
     * @param string $oldName
557
     * @param string $newName
558
     */
559
    private function updateRuleActions(string $oldName, string $newName): void
0 ignored issues
show
The method updateRuleActions() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
560
    {
561
        $types   = ['add_tag', 'remove_tag'];
562
        $actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id')
563
                             ->where('rules.user_id', $this->user->id)
564
                             ->whereIn('rule_actions.action_type', $types)
565
                             ->where('rule_actions.action_value', $oldName)
566
                             ->get(['rule_actions.*']);
567
        Log::debug(sprintf('Found %d actions to update.', $actions->count()));
568
        /** @var RuleAction $action */
569
        foreach ($actions as $action) {
570
            $action->action_value = $newName;
571
            $action->save();
572
            Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
573
        }
574
    }
575
576
    /**
577
     * @param string $oldName
578
     * @param string $newName
579
     */
580
    private function updateRuleTriggers(string $oldName, string $newName): void
0 ignored issues
show
The method updateRuleTriggers() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
581
    {
582
        $types    = ['tag_is',];
583
        $triggers = RuleTrigger::leftJoin('rules', 'rules.id', '=', 'rule_triggers.rule_id')
584
                               ->where('rules.user_id', $this->user->id)
585
                               ->whereIn('rule_triggers.trigger_type', $types)
586
                               ->where('rule_triggers.trigger_value', $oldName)
587
                               ->get(['rule_triggers.*']);
588
        Log::debug(sprintf('Found %d triggers to update.', $triggers->count()));
589
        /** @var RuleTrigger $trigger */
590
        foreach ($triggers as $trigger) {
591
            $trigger->trigger_value = $newName;
592
            $trigger->save();
593
            Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value));
594
        }
595
    }
596
}
597