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 ( 37b02e...ebbbe1 )
by James
08:59
created

app/Http/Controllers/CategoryController.php (3 issues)

Severity
1
<?php
2
/**
3
 * CategoryController.php
4
 * Copyright (c) 2017 [email protected]
5
 *
6
 * This file is part of Firefly III.
7
 *
8
 * Firefly III is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * Firefly III 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 General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace FireflyIII\Http\Controllers;
24
25
use Carbon\Carbon;
26
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
27
use FireflyIII\Helpers\Filter\InternalTransferFilter;
28
use FireflyIII\Http\Requests\CategoryFormRequest;
29
use FireflyIII\Models\AccountType;
30
use FireflyIII\Models\Category;
31
use FireflyIII\Models\TransactionType;
32
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
33
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
34
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
35
use FireflyIII\Support\CacheProperties;
36
use Illuminate\Http\Request;
37
use Illuminate\Pagination\LengthAwarePaginator;
38
use Illuminate\Support\Collection;
39
use Log;
40
use Preferences;
41
use Steam;
42
use View;
43
44
/**
45
 * Class CategoryController.
46
 */
47
class CategoryController extends Controller
48
{
49
    /** @var AccountRepositoryInterface */
50
    private $accountRepos;
51
    /** @var JournalRepositoryInterface */
52
    private $journalRepos;
53
    /** @var CategoryRepositoryInterface */
54
    private $repository;
55
56
    /**
57
     *
58
     */
59
    public function __construct()
60
    {
61
        parent::__construct();
62
63
        $this->middleware(
64
            function ($request, $next) {
65
                app('view')->share('title', trans('firefly.categories'));
66
                app('view')->share('mainTitleIcon', 'fa-bar-chart');
67
                $this->journalRepos = app(JournalRepositoryInterface::class);
68
                $this->repository   = app(CategoryRepositoryInterface::class);
69
                $this->accountRepos = app(AccountRepositoryInterface::class);
70
71
                return $next($request);
72
            }
73
        );
74
    }
75
76
    /**
77
     * @param Request $request
78
     *
79
     * @return View
80
     */
81
    public function create(Request $request)
82
    {
83
        if (true !== session('categories.create.fromStore')) {
84
            $this->rememberPreviousUri('categories.create.uri');
85
        }
86
        $request->session()->forget('categories.create.fromStore');
87
        $subTitle = trans('firefly.create_new_category');
88
89
        return view('categories.create', compact('subTitle'));
90
    }
91
92
    /**
93
     * @param Category $category
94
     *
95
     * @return View
96
     */
97
    public function delete(Category $category)
98
    {
99
        $subTitle = trans('firefly.delete_category', ['name' => $category->name]);
100
101
        // put previous url in session
102
        $this->rememberPreviousUri('categories.delete.uri');
103
104
        return view('categories.delete', compact('category', 'subTitle'));
105
    }
106
107
    /**
108
     * @param Request  $request
109
     * @param Category $category
110
     *
111
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
112
     */
113
    public function destroy(Request $request, Category $category)
114
    {
115
        $name = $category->name;
116
        $this->repository->destroy($category);
117
118
        $request->session()->flash('success', (string)trans('firefly.deleted_category', ['name' => $name]));
119
        Preferences::mark();
120
121
        return redirect($this->getPreviousUri('categories.delete.uri'));
122
    }
123
124
    /**
125
     * @param Request  $request
126
     * @param Category $category
127
     *
128
     * @return View
129
     */
130
    public function edit(Request $request, Category $category)
131
    {
132
        $subTitle = trans('firefly.edit_category', ['name' => $category->name]);
133
134
        // put previous url in session if not redirect from store (not "return_to_edit").
135
        if (true !== session('categories.edit.fromUpdate')) {
136
            $this->rememberPreviousUri('categories.edit.uri');
137
        }
138
        $request->session()->forget('categories.edit.fromUpdate');
139
140
        return view('categories.edit', compact('category', 'subTitle'));
141
    }
142
143
    /**
144
     * @param Request $request
145
     *
146
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
147
     */
148
    public function index(Request $request)
149
    {
150
        $page       = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
151
        $pageSize   = (int)Preferences::get('listPageSize', 50)->data;
152
        $collection = $this->repository->getCategories();
153
        $total      = $collection->count();
154
        $collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
155
156
        $collection->each(
157
            function (Category $category) {
158
                $category->lastActivity = $this->repository->lastUseDate($category, new Collection);
159
            }
160
        );
161
162
        // paginate categories
163
        $categories = new LengthAwarePaginator($collection, $total, $pageSize, $page);
164
        $categories->setPath(route('categories.index'));
165
166
        return view('categories.index', compact('categories'));
167
    }
168
169
    /**
170
     * @param Request $request
171
     * @param string  $moment
172
     *
173
     * @return View
174
     */
175
    public function noCategory(Request $request, string $moment = '')
176
    {
177
        // default values:
178
        $range    = Preferences::get('viewRange', '1M')->data;
179
        $start    = null;
180
        $end      = null;
181
        $periods  = new Collection;
182
        $page     = (int)$request->get('page');
183
        $pageSize = (int)Preferences::get('listPageSize', 50)->data;
184
185
        // prep for "all" view.
186
        if ('all' === $moment) {
187
            $subTitle = trans('firefly.all_journals_without_category');
188
            $first    = $this->journalRepos->first();
0 ignored issues
show
Deprecated Code introduced by
The function FireflyIII\Repositories\...itoryInterface::first() has been deprecated. ( Ignorable by Annotation )

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

188
            $first    = /** @scrutinizer ignore-deprecated */ $this->journalRepos->first();
Loading history...
189
            $start    = $first->date ?? new Carbon;
190
            $end      = new Carbon;
191
        }
192
193
        // prep for "specific date" view.
194
        if (strlen($moment) > 0 && 'all' !== $moment) {
195
            $start    = app('navigation')->startOfPeriod(new Carbon($moment), $range);
196
            $end      = app('navigation')->endOfPeriod($start, $range);
197
            $subTitle = trans(
198
                'firefly.without_category_between',
199
                ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
200
            );
201
            $periods  = $this->getNoCategoryPeriodOverview($start);
202
        }
203
204
        // prep for current period
205
        if (0 === strlen($moment)) {
206
            $start    = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
207
            $end      = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
208
            $periods  = $this->getNoCategoryPeriodOverview($start);
209
            $subTitle = trans(
210
                'firefly.without_category_between',
211
                ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
212
            );
213
        }
214
215
        /** @var JournalCollectorInterface $collector */
216
        $collector = app(JournalCollectorInterface::class);
217
        $collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutCategory()->withOpposingAccount()
218
                  ->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
219
        $collector->removeFilter(InternalTransferFilter::class);
220
        $transactions = $collector->getPaginatedJournals();
221
        $transactions->setPath(route('categories.no-category'));
222
223
        return view('categories.no-category', compact('transactions', 'subTitle', 'moment', 'periods', 'start', 'end'));
224
    }
225
226
    /**
227
     * @param Request                     $request
228
     * @param CategoryRepositoryInterface $repository
229
     * @param Category                    $category
230
     * @param string                      $moment
231
     *
232
     * @return View
233
     */
234
    public function show(Request $request, CategoryRepositoryInterface $repository, Category $category, string $moment = '')
235
    {
236
        // default values:
237
        $subTitle     = $category->name;
238
        $subTitleIcon = 'fa-bar-chart';
239
        $page         = (int)$request->get('page');
240
        $pageSize     = (int)Preferences::get('listPageSize', 50)->data;
241
        $range        = Preferences::get('viewRange', '1M')->data;
242
        $start        = null;
243
        $end          = null;
244
        $periods      = new Collection;
245
        $path         = route('categories.show', [$category->id]);
246
247
        // prep for "all" view.
248
        if ('all' === $moment) {
249
            $subTitle = trans('firefly.all_journals_for_category', ['name' => $category->name]);
250
            $first    = $repository->firstUseDate($category);
251
            /** @var Carbon $start */
252
            $start = $first ?? new Carbon;
253
            $end   = new Carbon;
254
            $path  = route('categories.show', [$category->id, 'all']);
255
        }
256
257
        // prep for "specific date" view.
258
        if (strlen($moment) > 0 && 'all' !== $moment) {
259
            $start    = app('navigation')->startOfPeriod(new Carbon($moment), $range);
260
            $end      = app('navigation')->endOfPeriod($start, $range);
261
            $subTitle = trans(
262
                'firefly.journals_in_period_for_category',
263
                ['name'  => $category->name,
264
                 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),]
265
            );
266
            $periods  = $this->getPeriodOverview($category, $start);
267
            $path     = route('categories.show', [$category->id, $moment]);
268
        }
269
270
        // prep for current period
271
        if (0 === strlen($moment)) {
272
            /** @var Carbon $start */
273
            $start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
274
            /** @var Carbon $end */
275
            $end      = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
276
            $periods  = $this->getPeriodOverview($category, $start);
277
            $subTitle = trans(
278
                'firefly.journals_in_period_for_category',
279
                ['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
280
                 'end'  => $end->formatLocalized($this->monthAndDayFormat),]
281
            );
282
        }
283
284
        /** @var JournalCollectorInterface $collector */
285
        $collector = app(JournalCollectorInterface::class);
286
        $collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
287
                  ->setCategory($category)->withBudgetInformation()->withCategoryInformation();
288
        $collector->removeFilter(InternalTransferFilter::class);
289
        $transactions = $collector->getPaginatedJournals();
290
        $transactions->setPath($path);
291
292
        return view('categories.show', compact('category', 'moment', 'transactions', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end'));
293
    }
294
295
    /**
296
     * @param CategoryFormRequest         $request
297
     * @param CategoryRepositoryInterface $repository
298
     *
299
     * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
300
     */
301
    public function store(CategoryFormRequest $request, CategoryRepositoryInterface $repository)
302
    {
303
        $data     = $request->getCategoryData();
304
        $category = $repository->store($data);
305
306
        $request->session()->flash('success', (string)trans('firefly.stored_category', ['name' => $category->name]));
307
        Preferences::mark();
308
309
        if (1 === (int)$request->get('create_another')) {
310
            // @codeCoverageIgnoreStart
311
            $request->session()->put('categories.create.fromStore', true);
312
313
            return redirect(route('categories.create'))->withInput();
314
            // @codeCoverageIgnoreEnd
315
        }
316
317
        return redirect(route('categories.index'));
318
    }
319
320
    /**
321
     * @param CategoryFormRequest         $request
322
     * @param CategoryRepositoryInterface $repository
323
     * @param Category                    $category
324
     *
325
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
326
     */
327
    public function update(CategoryFormRequest $request, CategoryRepositoryInterface $repository, Category $category)
328
    {
329
        $data = $request->getCategoryData();
330
        $repository->update($category, $data);
331
332
        $request->session()->flash('success', (string)trans('firefly.updated_category', ['name' => $category->name]));
333
        Preferences::mark();
334
335
        if (1 === (int)$request->get('return_to_edit')) {
336
            // @codeCoverageIgnoreStart
337
            $request->session()->put('categories.edit.fromUpdate', true);
338
339
            return redirect(route('categories.edit', [$category->id]));
340
            // @codeCoverageIgnoreEnd
341
        }
342
343
        return redirect($this->getPreviousUri('categories.edit.uri'));
344
    }
345
346
    /**
347
     * @param Carbon $theDate
348
     *
349
     * @return Collection
350
     */
351
    private function getNoCategoryPeriodOverview(Carbon $theDate): Collection
352
    {
353
        $range = Preferences::get('viewRange', '1M')->data;
354
        $first = $this->journalRepos->first();
0 ignored issues
show
Deprecated Code introduced by
The function FireflyIII\Repositories\...itoryInterface::first() has been deprecated. ( Ignorable by Annotation )

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

354
        $first = /** @scrutinizer ignore-deprecated */ $this->journalRepos->first();
Loading history...
355
        $start = $first->date ?? new Carbon;
356
        $end   = $theDate ?? new Carbon;
357
358
        // properties for cache
359
        $cache = new CacheProperties;
360
        $cache->addProperty($start);
361
        $cache->addProperty($end);
362
        $cache->addProperty('no-category-period-entries');
363
364
        if ($cache->has()) {
365
            return $cache->get(); // @codeCoverageIgnore
366
        }
367
368
        $dates   = app('navigation')->blockPeriods($start, $end, $range);
369
        $entries = new Collection;
370
371
        foreach ($dates as $date) {
372
373
            // count journals without category in this period:
374
            /** @var JournalCollectorInterface $collector */
375
            $collector = app(JournalCollectorInterface::class);
376
            $collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
377
                      ->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
378
            $collector->removeFilter(InternalTransferFilter::class);
379
            $count = $collector->getJournals()->count();
380
381
            // amount transferred
382
            /** @var JournalCollectorInterface $collector */
383
            $collector = app(JournalCollectorInterface::class);
384
            $collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
385
                      ->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
386
            $collector->removeFilter(InternalTransferFilter::class);
387
            $transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
388
389
            // amount spent
390
            /** @var JournalCollectorInterface $collector */
391
            $collector = app(JournalCollectorInterface::class);
392
            $collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
393
                [TransactionType::WITHDRAWAL]
394
            );
395
            $spent = $collector->getJournals()->sum('transaction_amount');
396
397
            // amount earned
398
            /** @var JournalCollectorInterface $collector */
399
            $collector = app(JournalCollectorInterface::class);
400
            $collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
401
                [TransactionType::DEPOSIT]
402
            );
403
            $earned   = $collector->getJournals()->sum('transaction_amount');
404
            $dateStr  = $date['end']->format('Y-m-d');
405
            $dateName = app('navigation')->periodShow($date['end'], $date['period']);
406
            $entries->push(
407
                [
408
                    'string'      => $dateStr,
409
                    'name'        => $dateName,
410
                    'count'       => $count,
411
                    'spent'       => $spent,
412
                    'earned'      => $earned,
413
                    'transferred' => $transferred,
414
                    'date'        => clone $date['end'],
415
                ]
416
            );
417
        }
418
        Log::debug('End of loops');
419
        $cache->store($entries);
420
421
        return $entries;
422
    }
423
424
    /**
425
     * @param Category $category
426
     *
427
     * @param Carbon   $date
428
     *
429
     * @return Collection
430
     */
431
    private function getPeriodOverview(Category $category, Carbon $date): Collection
432
    {
433
        $range    = Preferences::get('viewRange', '1M')->data;
434
        $first    = $this->journalRepos->first();
0 ignored issues
show
Deprecated Code introduced by
The function FireflyIII\Repositories\...itoryInterface::first() has been deprecated. ( Ignorable by Annotation )

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

434
        $first    = /** @scrutinizer ignore-deprecated */ $this->journalRepos->first();
Loading history...
435
        $start    = $first->date ?? new Carbon;
436
        $end      = $date ?? new Carbon;
437
        $accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
438
439
        // properties for entries with their amounts.
440
        $cache = new CacheProperties();
441
        $cache->addProperty($start);
442
        $cache->addProperty($end);
443
        $cache->addProperty($range);
444
        $cache->addProperty('categories.entries');
445
        $cache->addProperty($category->id);
446
447
        if ($cache->has()) {
448
            return $cache->get(); // @codeCoverageIgnore
449
        }
450
        /** @var array $dates */
451
        $dates   = app('navigation')->blockPeriods($start, $end, $range);
452
        $entries = new Collection;
453
454
        foreach ($dates as $currentDate) {
455
            $spent    = $this->repository->spentInPeriod(new Collection([$category]), $accounts, $currentDate['start'], $currentDate['end']);
456
            $earned   = $this->repository->earnedInPeriod(new Collection([$category]), $accounts, $currentDate['start'], $currentDate['end']);
457
            $dateStr  = $currentDate['end']->format('Y-m-d');
458
            $dateName = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
459
460
            // amount transferred
461
            /** @var JournalCollectorInterface $collector */
462
            $collector = app(JournalCollectorInterface::class);
463
            $collector->setAllAssetAccounts()->setRange($currentDate['start'], $currentDate['end'])->setCategory($category)
464
                      ->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
465
            $collector->removeFilter(InternalTransferFilter::class);
466
            $transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
467
468
            $entries->push(
469
                [
470
                    'string'      => $dateStr,
471
                    'name'        => $dateName,
472
                    'spent'       => $spent,
473
                    'earned'      => $earned,
474
                    'sum'         => bcadd($earned, $spent),
475
                    'transferred' => $transferred,
476
                    'date'        => clone $currentDate['end'],
477
                ]
478
            );
479
        }
480
        $cache->store($entries);
481
482
        return $entries;
483
    }
484
}
485