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/Http/Controllers/Chart/BudgetController.php (10 issues)

1
<?php
2
/**
3
 * BudgetController.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\Http\Controllers\Chart;
24
25
use Carbon\Carbon;
26
use FireflyIII\Exceptions\FireflyException;
27
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
28
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
29
use FireflyIII\Http\Controllers\Controller;
30
use FireflyIII\Models\Budget;
31
use FireflyIII\Models\BudgetLimit;
32
use FireflyIII\Models\TransactionCurrency;
33
use FireflyIII\Models\TransactionType;
34
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
35
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
36
use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface;
37
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
38
use FireflyIII\Support\CacheProperties;
39
use FireflyIII\Support\Http\Controllers\AugumentData;
40
use FireflyIII\Support\Http\Controllers\DateCalculation;
41
use Illuminate\Http\JsonResponse;
42
use Illuminate\Support\Collection;
43
44
/**
45
 * Class BudgetController.
46
 *
47
 */
48
class BudgetController extends Controller
49
{
50
    use DateCalculation, AugumentData;
51
    /** @var GeneratorInterface Chart generation methods. */
52
    protected $generator;
53
    /** @var OperationsRepositoryInterface */
54
    protected $opsRepository;
55
    /** @var BudgetRepositoryInterface The budget repository */
56
    protected $repository;
57
    /** @var BudgetLimitRepositoryInterface */
58
    private $blRepository;
59
    /** @var NoBudgetRepositoryInterface */
60
    private $nbRepository;
61
62
    /**
63
     * BudgetController constructor.
64
     *
65
     * @codeCoverageIgnore
66
     */
67
    public function __construct()
68
    {
69
        parent::__construct();
70
71
        $this->middleware(
72
            function ($request, $next) {
73
                $this->generator     = app(GeneratorInterface::class);
74
                $this->repository    = app(BudgetRepositoryInterface::class);
75
                $this->opsRepository = app(OperationsRepositoryInterface::class);
76
                $this->blRepository  = app(BudgetLimitRepositoryInterface::class);
77
                $this->nbRepository  = app(NoBudgetRepositoryInterface::class);
78
79
                return $next($request);
80
            }
81
        );
82
    }
83
84
85
    /**
86
     * Shows overview of a single budget.
87
     *
88
     * @param Budget $budget
89
     *
90
     * @return JsonResponse
91
     */
92
    public function budget(Budget $budget): JsonResponse
93
    {
94
        /** @var Carbon $start */
95
        $start = $this->repository->firstUseDate($budget) ?? session('start', new Carbon);
96
        /** @var Carbon $end */
97
        $end   = session('end', new Carbon);
98
        $cache = new CacheProperties();
99
        $cache->addProperty($start);
100
        $cache->addProperty($end);
101
        $cache->addProperty('chart.budget.budget');
102
        $cache->addProperty($budget->id);
103
104
        if ($cache->has()) {
105
            return response()->json($cache->get()); // @codeCoverageIgnore
106
        }
107
        $step           = $this->calculateStep($start, $end); // depending on diff, do something with range of chart.
108
        $collection     = new Collection([$budget]);
109
        $chartData      = [];
110
        $loopStart      = clone $start;
111
        $loopStart      = app('navigation')->startOfPeriod($loopStart, $step);
112
        $currencies     = [];
113
        $defaultEntries = [];
114
        //        echo '<hr>';
115
        while ($end >= $loopStart) {
116
            /** @var Carbon $currentEnd */
117
            $loopEnd = app('navigation')->endOfPeriod($loopStart, $step);
118
            if ('1Y' === $step) {
119
                $loopEnd->subDay(); // @codeCoverageIgnore
120
            }
121
            $spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection);
122
            $label = trim(app('navigation')->periodShow($loopStart, $step));
123
124
            foreach ($spent as $row) {
125
                $currencyId              = $row['currency_id'];
126
                $currencies[$currencyId] = $currencies[$currencyId] ?? $row; // don't mind the field 'sum'
127
                // also store this day's sum:
128
                $currencies[$currencyId]['spent'][$label] = $row['sum'];
129
            }
130
            $defaultEntries[$label] = 0;
131
            // set loop start to the next period:
132
            $loopStart = clone $loopEnd;
133
            $loopStart->addSecond();
134
        }
135
        // loop all currencies:
136
        foreach ($currencies as $currencyId => $currency) {
137
            $chartData[$currencyId] = [
138
                'label'           => count($currencies) > 1 ? sprintf('%s (%s)', $budget->name, $currency['currency_name']) : $budget->name,
139
                'type'            => 'bar',
140
                'currency_symbol' => $currency['currency_symbol'],
141
                'entries'         => $defaultEntries,
142
            ];
143
            foreach ($currency['spent'] as $label => $spent) {
144
                $chartData[$currencyId]['entries'][$label] = round(bcmul($spent, '-1'), $currency['currency_decimal_places']);
0 ignored issues
show
bcmul($spent, '-1') of type string is incompatible with the type double expected by parameter $val of round(). ( Ignorable by Annotation )

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

144
                $chartData[$currencyId]['entries'][$label] = round(/** @scrutinizer ignore-type */ bcmul($spent, '-1'), $currency['currency_decimal_places']);
Loading history...
145
            }
146
        }
147
        $data = $this->generator->multiSet(array_values($chartData));
148
        $cache->store($data);
149
150
        return response()->json($data);
151
    }
152
153
154
    /**
155
     * Shows the amount left in a specific budget limit.
156
     *
157
     * @param Budget      $budget
158
     * @param BudgetLimit $budgetLimit
159
     *
160
     * @return JsonResponse
161
     *
162
     * @throws FireflyException
163
     */
164
    public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit): JsonResponse
165
    {
166
        if ($budgetLimit->budget->id !== $budget->id) {
167
            throw new FireflyException('This budget limit is not part of this budget.');
168
        }
169
170
        $start = clone $budgetLimit->start_date;
171
        $end   = clone $budgetLimit->end_date;
172
        $cache = new CacheProperties();
173
        $cache->addProperty($start);
174
        $cache->addProperty($end);
175
        $cache->addProperty('chart.budget.budget.limit');
176
        $cache->addProperty($budgetLimit->id);
177
        $cache->addProperty($budget->id);
178
179
        if ($cache->has()) {
180
            return response()->json($cache->get()); // @codeCoverageIgnore
181
        }
182
183
        $entries          = [];
184
        $amount           = $budgetLimit->amount;
185
        $budgetCollection = new Collection([$budget]);
186
        while ($start <= $end) {
187
            $spent            = $this->opsRepository->spentInPeriod($budgetCollection, new Collection, $start, $start);
188
            $amount           = bcadd($amount, $spent);
189
            $format           = $start->formatLocalized((string)trans('config.month_and_day'));
190
            $entries[$format] = $amount;
191
192
            $start->addDay();
193
        }
194
        $data = $this->generator->singleSet((string)trans('firefly.left'), $entries);
195
        // add currency symbol from budget limit:
196
        $data['datasets'][0]['currency_symbol'] = $budgetLimit->transactionCurrency->symbol;
197
        $cache->store($data);
198
199
        return response()->json($data);
200
    }
201
202
203
    /**
204
     * Shows how much is spent per asset account.
205
     *
206
     * @param Budget           $budget
207
     * @param BudgetLimit|null $budgetLimit
208
     *
209
     * @return JsonResponse
210
     */
211
    public function expenseAsset(Budget $budget, ?BudgetLimit $budgetLimit = null): JsonResponse
212
    {
213
        /** @var GroupCollectorInterface $collector */
214
        $collector     = app(GroupCollectorInterface::class);
215
        $budgetLimitId = null === $budgetLimit ? 0 : $budgetLimit->id;
216
        $cache         = new CacheProperties;
217
        $cache->addProperty($budget->id);
218
        $cache->addProperty($budgetLimitId);
219
        $cache->addProperty('chart.budget.expense-asset');
220
        $collector->setRange(session()->get('start'), session()->get('end'));
221
        $start = session()->get('start');
222
        $end   = session()->get('end');
223
        if (null !== $budgetLimit) {
224
            $start = $budgetLimit->start_date;
225
            $end   = $budgetLimit->end_date;
226
            $collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
0 ignored issues
show
$budgetLimit->end_date of type string is incompatible with the type Carbon\Carbon expected by parameter $end of FireflyIII\Helpers\Colle...orInterface::setRange(). ( Ignorable by Annotation )

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

226
            $collector->setRange($budgetLimit->start_date, /** @scrutinizer ignore-type */ $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
Loading history...
227
        }
228
        $cache->addProperty($start);
229
        $cache->addProperty($end);
230
231
        if ($cache->has()) {
232
            return response()->json($cache->get()); // @codeCoverageIgnore
233
        }
234
        $collector->setBudget($budget);
235
        $journals  = $collector->getExtractedJournals();
236
        $result    = [];
237
        $chartData = [];
238
239
        // group by asset account ID:
240
        foreach ($journals as $journal) {
241
            $key                    = sprintf('%d-%d', (int)$journal['source_account_id'], $journal['currency_id']);
242
            $result[$key]           = $result[$key] ?? [
243
                    'amount'          => '0',
244
                    'currency_symbol' => $journal['currency_symbol'],
245
                    'currency_name'   => $journal['currency_name'],
246
                ];
247
            $result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
248
        }
249
250
        $names = $this->getAccountNames(array_keys($result));
251
        foreach ($result as $combinedId => $info) {
252
            $parts   = explode('-', $combinedId);
253
            $assetId = (int)$parts[0];
254
            $title   = sprintf('%s (%s)', $names[$assetId] ?? '(empty)', $info['currency_name']);
255
            $chartData[$title]
256
                     = [
257
                'amount'          => $info['amount'],
258
                'currency_symbol' => $info['currency_symbol'],
259
            ];
260
        }
261
262
        $data = $this->generator->multiCurrencyPieChart($chartData);
263
        $cache->store($data);
264
265
        return response()->json($data);
266
    }
267
268
269
    /**
270
     * Shows how much is spent per category.
271
     *
272
     * @param Budget           $budget
273
     * @param BudgetLimit|null $budgetLimit
274
     *
275
     * @return JsonResponse
276
     */
277
    public function expenseCategory(Budget $budget, ?BudgetLimit $budgetLimit = null): JsonResponse
278
    {
279
        /** @var GroupCollectorInterface $collector */
280
        $collector     = app(GroupCollectorInterface::class);
281
        $budgetLimitId = null === $budgetLimit ? 0 : $budgetLimit->id;
282
        $cache         = new CacheProperties;
283
        $cache->addProperty($budget->id);
284
        $cache->addProperty($budgetLimitId);
285
        $cache->addProperty('chart.budget.expense-category');
286
        $collector->setRange(session()->get('start'), session()->get('end'));
287
        $start = session()->get('start');
288
        $end   = session()->get('end');
289
        if (null !== $budgetLimit) {
290
            $start = $budgetLimit->start_date;
291
            $end   = $budgetLimit->end_date;
292
            $collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
0 ignored issues
show
$budgetLimit->end_date of type string is incompatible with the type Carbon\Carbon expected by parameter $end of FireflyIII\Helpers\Colle...orInterface::setRange(). ( Ignorable by Annotation )

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

292
            $collector->setRange($budgetLimit->start_date, /** @scrutinizer ignore-type */ $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
Loading history...
293
        }
294
        $cache->addProperty($start);
295
        $cache->addProperty($end);
296
297
        if ($cache->has()) {
298
            return response()->json($cache->get()); // @codeCoverageIgnore
299
        }
300
        $collector->setBudget($budget)->withCategoryInformation();
301
        $journals  = $collector->getExtractedJournals();
302
        $result    = [];
303
        $chartData = [];
304
        foreach ($journals as $journal) {
305
            $key                    = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
306
            $result[$key]           = $result[$key] ?? [
307
                    'amount'          => '0',
308
                    'currency_symbol' => $journal['currency_symbol'],
309
                    'currency_name'   => $journal['currency_name'],
310
                ];
311
            $result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
312
        }
313
314
        $names = $this->getCategoryNames(array_keys($result));
315
        foreach ($result as $combinedId => $info) {
316
            $parts             = explode('-', $combinedId);
317
            $categoryId        = (int)$parts[0];
318
            $title             = sprintf('%s (%s)', $names[$categoryId] ?? '(empty)', $info['currency_name']);
319
            $chartData[$title] = [
320
                'amount'          => $info['amount'],
321
                'currency_symbol' => $info['currency_symbol'],
322
            ];
323
        }
324
        $data = $this->generator->multiCurrencyPieChart($chartData);
325
        $cache->store($data);
326
327
        return response()->json($data);
328
    }
329
330
331
    /**
332
     * Shows how much is spent per expense account.
333
     *
334
     *
335
     * @param Budget           $budget
336
     * @param BudgetLimit|null $budgetLimit
337
     *
338
     * @return JsonResponse
339
     */
340
    public function expenseExpense(Budget $budget, ?BudgetLimit $budgetLimit = null): JsonResponse
341
    {
342
        /** @var GroupCollectorInterface $collector */
343
        $collector     = app(GroupCollectorInterface::class);
344
        $budgetLimitId = null === $budgetLimit ? 0 : $budgetLimit->id;
345
        $cache         = new CacheProperties;
346
        $cache->addProperty($budget->id);
347
        $cache->addProperty($budgetLimitId);
348
        $cache->addProperty('chart.budget.expense-expense');
349
        $collector->setRange(session()->get('start'), session()->get('end'));
350
        $start = session()->get('start');
351
        $end   = session()->get('end');
352
        if (null !== $budgetLimit) {
353
            $start = $budgetLimit->start_date;
354
            $end   = $budgetLimit->end_date;
355
            $collector->setRange($budgetLimit->start_date, $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
0 ignored issues
show
$budgetLimit->end_date of type string is incompatible with the type Carbon\Carbon expected by parameter $end of FireflyIII\Helpers\Colle...orInterface::setRange(). ( Ignorable by Annotation )

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

355
            $collector->setRange($budgetLimit->start_date, /** @scrutinizer ignore-type */ $budgetLimit->end_date)->setCurrency($budgetLimit->transactionCurrency);
Loading history...
356
        }
357
        $cache->addProperty($start);
358
        $cache->addProperty($end);
359
360
        if ($cache->has()) {
361
            return response()->json($cache->get()); // @codeCoverageIgnore
362
        }
363
364
        $collector->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withAccountInformation();
365
        $journals  = $collector->getExtractedJournals();
366
        $result    = [];
367
        $chartData = [];
368
        /** @var array $journal */
369
        foreach ($journals as $journal) {
370
            $key                    = sprintf('%d-%d', $journal['destination_account_id'], $journal['currency_id']);
371
            $result[$key]           = $result[$key] ?? [
372
                    'amount'          => '0',
373
                    'currency_symbol' => $journal['currency_symbol'],
374
                    'currency_name'   => $journal['currency_name'],
375
                ];
376
            $result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
377
        }
378
379
        $names = $this->getAccountNames(array_keys($result));
380
        foreach ($result as $combinedId => $info) {
381
            $parts             = explode('-', $combinedId);
382
            $opposingId        = (int)$parts[0];
383
            $name              = $names[$opposingId] ?? 'no name';
384
            $title             = sprintf('%s (%s)', $name, $info['currency_name']);
385
            $chartData[$title] = [
386
                'amount'          => $info['amount'],
387
                'currency_symbol' => $info['currency_symbol'],
388
            ];
389
        }
390
391
        $data = $this->generator->multiCurrencyPieChart($chartData);
392
        $cache->store($data);
393
394
        return response()->json($data);
395
    }
396
397
398
    /**
399
     * Shows a budget list with spent/left/overspent.
400
     *
401
     * TODO there are cases when this chart hides expenses: when budget has limits
402
     * and limits are found and used, but the expense is in another currency.
403
     *
404
     * @return JsonResponse
405
     *
406
     */
407
    public function frontpage(): JsonResponse
408
    {
409
        $start = session('start', Carbon::now()->startOfMonth());
410
        $end   = session('end', Carbon::now()->endOfMonth());
411
        // chart properties for cache:
412
        $cache = new CacheProperties();
413
        $cache->addProperty($start);
414
        $cache->addProperty($end);
415
        $cache->addProperty('chart.budget.frontpage');
416
        if ($cache->has()) {
417
            // return response()->json($cache->get()); // @codeCoverageIgnore
418
        }
419
        $budgets   = $this->repository->getActiveBudgets();
420
        $chartData = [
421
            ['label' => (string)trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'],
422
            ['label' => (string)trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'],
423
            ['label' => (string)trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'],
424
        ];
425
426
        /** @var Budget $budget */
427
        foreach ($budgets as $budget) {
428
            $limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
0 ignored issues
show
It seems like $start can also be of type Illuminate\Session\SessionManager and Illuminate\Session\Store; however, parameter $start of FireflyIII\Repositories\...face::getBudgetLimits() does only seem to accept Carbon\Carbon|null, maybe add an additional type check? ( Ignorable by Annotation )

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

428
            $limits = $this->blRepository->getBudgetLimits($budget, /** @scrutinizer ignore-type */ $start, $end);
Loading history...
It seems like $end can also be of type Illuminate\Session\SessionManager and Illuminate\Session\Store; however, parameter $end of FireflyIII\Repositories\...face::getBudgetLimits() does only seem to accept Carbon\Carbon|null, maybe add an additional type check? ( Ignorable by Annotation )

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

428
            $limits = $this->blRepository->getBudgetLimits($budget, $start, /** @scrutinizer ignore-type */ $end);
Loading history...
429
            if (0 === $limits->count()) {
430
                $spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]), null);
0 ignored issues
show
It seems like $end can also be of type Illuminate\Session\SessionManager and Illuminate\Session\Store; however, parameter $end of FireflyIII\Repositories\...nterface::sumExpenses() does only seem to accept Carbon\Carbon, maybe add an additional type check? ( Ignorable by Annotation )

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

430
                $spent = $this->opsRepository->sumExpenses($start, /** @scrutinizer ignore-type */ $end, null, new Collection([$budget]), null);
Loading history...
It seems like $start can also be of type Illuminate\Session\SessionManager and Illuminate\Session\Store; however, parameter $start of FireflyIII\Repositories\...nterface::sumExpenses() does only seem to accept Carbon\Carbon, maybe add an additional type check? ( Ignorable by Annotation )

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

430
                $spent = $this->opsRepository->sumExpenses(/** @scrutinizer ignore-type */ $start, $end, null, new Collection([$budget]), null);
Loading history...
431
                /** @var array $entry */
432
                foreach ($spent as $entry) {
433
                    $title                           = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
434
                    $chartData[0]['entries'][$title] = bcmul($entry['sum'], '-1'); // spent
435
                    $chartData[1]['entries'][$title] = 0; // left to spend
436
                    $chartData[2]['entries'][$title] = 0; // overspent
437
                }
438
            }
439
            if (0 !== $limits->count()) {
440
                /** @var BudgetLimit $limit */
441
                foreach ($limits as $limit) {
442
                    $spent = $this->opsRepository->sumExpenses(
443
                        $limit->start_date,
444
                        $limit->end_date,
445
                        null,
446
                        new Collection([$budget]),
447
                        $limit->transactionCurrency
448
                    );
449
                    /** @var array $entry */
450
                    foreach ($spent as $entry) {
451
                        $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
452
                        if ($limit->start_date->startOfDay()->ne($start->startOfDay()) || $limit->end_date->startOfDay()->ne($end->startOfDay())) {
453
                            $title = sprintf(
454
                                '%s (%s) (%s - %s)',
455
                                $budget->name,
456
                                $entry['currency_name'],
457
                                $limit->start_date->formatLocalized($this->monthAndDayFormat),
458
                                $limit->end_date->formatLocalized($this->monthAndDayFormat)
459
                            );
460
                        }
461
                        $sumSpent                        = bcmul($entry['sum'], '-1'); // spent
462
                        $chartData[0]['entries'][$title] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent;
463
                        $chartData[1]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($entry['sum'], $limit->amount)
464
                            : '0';
465
                        $chartData[2]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ?
466
                            '0' : bcmul(bcadd($entry['sum'], $limit->amount), '-1');
467
                    }
468
                }
469
            }
470
        }
471
        $data = $this->generator->multiSet($chartData);
472
        $cache->store($data);
473
474
        return response()->json($data);
475
    }
476
477
478
    /**
479
     * Shows a budget overview chart (spent and budgeted).
480
     *
481
     * @param Budget              $budget
482
     * @param TransactionCurrency $currency
483
     * @param Collection          $accounts
484
     * @param Carbon              $start
485
     * @param Carbon              $end
486
     *
487
     * @return JsonResponse
488
     */
489
    public function period(Budget $budget, TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse
490
    {
491
        // chart properties for cache:
492
        $cache = new CacheProperties();
493
        $cache->addProperty($start);
494
        $cache->addProperty($end);
495
        $cache->addProperty($accounts);
496
        $cache->addProperty($budget->id);
497
        $cache->addProperty($currency->id);
498
        $cache->addProperty('chart.budget.period');
499
        if ($cache->has()) {
500
            return response()->json($cache->get()); // @codeCoverageIgnore
501
        }
502
        $titleFormat    = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
503
        $preferredRange = app('navigation')->preferredRangeFormat($start, $end);
504
        $chartData      = [
505
            [
506
                'label'           => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency->name]),
507
                'type'            => 'bar',
508
                'entries'         => [],
509
                'currency_symbol' => $currency->symbol,
510
            ],
511
            [
512
                'label'           => (string)trans('firefly.box_budgeted_in_currency', ['currency' => $currency->name]),
513
                'type'            => 'bar',
514
                'currency_symbol' => $currency->symbol,
515
                'entries'         => [],
516
            ],
517
        ];
518
519
        $currentStart = clone $start;
520
        while ($currentStart <= $end) {
521
            $currentStart = app('navigation')->startOfPeriod($currentStart, $preferredRange);
522
            $title        = $currentStart->formatLocalized($titleFormat);
523
            $currentEnd   = app('navigation')->endOfPeriod($currentStart, $preferredRange);
524
525
            // default limit is no limit:
526
            $chartData[0]['entries'][$title] = 0;
527
528
            // default spent is not spent at all.
529
            $chartData[1]['entries'][$title] = 0;
530
531
            // get budget limit in this period for this currency.
532
            $limit = $this->blRepository->find($budget, $currency, $currentStart, $currentEnd);
533
            if (null !== $limit) {
534
                $chartData[1]['entries'][$title] = round($limit->amount, $currency->decimal_places);
0 ignored issues
show
$limit->amount of type string is incompatible with the type double expected by parameter $val of round(). ( Ignorable by Annotation )

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

534
                $chartData[1]['entries'][$title] = round(/** @scrutinizer ignore-type */ $limit->amount, $currency->decimal_places);
Loading history...
535
            }
536
537
            // get spent amount in this period for this currency.
538
            $sum                             = $this->opsRepository->sumExpenses($currentStart, $currentEnd, $accounts, new Collection([$budget]), $currency);
539
            $amount                          = app('steam')->positive($sum[$currency->id]['sum'] ?? '0');
540
            $chartData[0]['entries'][$title] = round($amount, $currency->decimal_places);
541
542
            $currentStart = clone $currentEnd;
543
            $currentStart->addDay()->startOfDay();
544
        }
545
546
        $data = $this->generator->multiSet($chartData);
547
        $cache->store($data);
548
549
        return response()->json($data);
550
    }
551
552
553
    /**
554
     * Shows a chart for transactions without a budget.
555
     *
556
     * @param TransactionCurrency $currency
557
     * @param Collection          $accounts
558
     * @param Carbon              $start
559
     * @param Carbon              $end
560
     *
561
     * @return JsonResponse
562
     */
563
    public function periodNoBudget(TransactionCurrency $currency, Collection $accounts, Carbon $start, Carbon $end): JsonResponse
564
    {
565
        // chart properties for cache:
566
        $cache = new CacheProperties();
567
        $cache->addProperty($start);
568
        $cache->addProperty($end);
569
        $cache->addProperty($accounts);
570
        $cache->addProperty($currency->id);
571
        $cache->addProperty('chart.budget.no-budget');
572
        if ($cache->has()) {
573
            return response()->json($cache->get()); // @codeCoverageIgnore
574
        }
575
576
        // the expenses:
577
        $titleFormat    = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
578
        $chartData      = [];
579
        $currentStart   = clone $start;
580
        $preferredRange = app('navigation')->preferredRangeFormat($start, $end);
581
        while ($currentStart <= $end) {
582
            $currentEnd        = app('navigation')->endOfPeriod($currentStart, $preferredRange);
583
            $title             = $currentStart->formatLocalized($titleFormat);
584
            $sum               = $this->nbRepository->sumExpenses($currentStart, $currentEnd, $accounts, $currency);
585
            $amount            = app('steam')->positive($sum[$currency->id]['sum'] ?? '0');
586
            $chartData[$title] = round($amount, $currency->decimal_places);
0 ignored issues
show
$amount of type string is incompatible with the type double expected by parameter $val of round(). ( Ignorable by Annotation )

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

586
            $chartData[$title] = round(/** @scrutinizer ignore-type */ $amount, $currency->decimal_places);
Loading history...
587
            $currentStart      = app('navigation')->addPeriod($currentStart, $preferredRange, 0);
588
        }
589
590
        $data = $this->generator->singleSet((string)trans('firefly.spent'), $chartData);
591
        $cache->store($data);
592
593
        return response()->json($data);
594
    }
595
}
596