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/Api/V1/Controllers/SummaryController.php (10 issues)

1
<?php
2
3
/**
4
 * SummaryController.php
5
 * Copyright (c) 2019 [email protected]
6
 *
7
 * This file is part of Firefly III (https://github.com/firefly-iii).
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program 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 Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
 */
22
23
declare(strict_types=1);
24
25
namespace FireflyIII\Api\V1\Controllers;
26
27
use Carbon\Carbon;
28
use Exception;
29
use FireflyIII\Api\V1\Requests\DateRequest;
30
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
31
use FireflyIII\Helpers\Report\NetWorthInterface;
32
use FireflyIII\Models\Account;
33
use FireflyIII\Models\AccountType;
34
use FireflyIII\Models\TransactionCurrency;
35
use FireflyIII\Models\TransactionType;
36
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
37
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
38
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
39
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
40
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
41
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
42
use FireflyIII\User;
43
use Illuminate\Http\JsonResponse;
44
45
/**
46
 * Class SummaryController
47
 */
48
class SummaryController extends Controller
49
{
50
    /** @var AvailableBudgetRepositoryInterface */
51
    private $abRepository;
52
    /** @var AccountRepositoryInterface */
53
    private $accountRepository;
54
    /** @var BillRepositoryInterface */
55
    private $billRepository;
56
    /** @var BudgetRepositoryInterface */
57
    private $budgetRepository;
58
    /** @var CurrencyRepositoryInterface */
59
    private $currencyRepos;
60
61
    /** @var OperationsRepositoryInterface */
62
    private $opsRepository;
63
64
    /**
65
     * SummaryController constructor.
66
     *
67
     * @codeCoverageIgnore
68
     */
69
    public function __construct()
70
    {
71
        parent::__construct();
72
        $this->middleware(
73
            function ($request, $next) {
74
                /** @var User $user */
75
                $user                    = auth()->user();
76
                $this->currencyRepos     = app(CurrencyRepositoryInterface::class);
77
                $this->billRepository    = app(BillRepositoryInterface::class);
78
                $this->budgetRepository  = app(BudgetRepositoryInterface::class);
79
                $this->accountRepository = app(AccountRepositoryInterface::class);
80
                $this->abRepository      = app(AvailableBudgetRepositoryInterface::class);
81
                $this->opsRepository     = app(OperationsRepositoryInterface::class);
82
83
                $this->billRepository->setUser($user);
84
                $this->currencyRepos->setUser($user);
85
                $this->budgetRepository->setUser($user);
86
                $this->accountRepository->setUser($user);
87
                $this->abRepository->setUser($user);
88
                $this->opsRepository->setUser($user);
89
90
91
                return $next($request);
92
            }
93
        );
94
    }
95
96
    /**
97
     * @param DateRequest $request
98
     *
99
     * @throws Exception
100
     * @return JsonResponse
101
     */
102
    public function basic(DateRequest $request): JsonResponse
103
    {
104
        // parameters for boxes:
105
        $dates = $request->getAll();
106
        $start = $dates['start'];
107
        $end   = $dates['end'];
108
        $code  = $request->get('currency_code');
109
110
        // balance information:
111
        $balanceData  = $this->getBalanceInformation($start, $end);
0 ignored issues
show
It seems like $start can also be of type null; however, parameter $start of FireflyIII\Api\V1\Contro...getBalanceInformation() 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

111
        $balanceData  = $this->getBalanceInformation(/** @scrutinizer ignore-type */ $start, $end);
Loading history...
It seems like $end can also be of type null; however, parameter $end of FireflyIII\Api\V1\Contro...getBalanceInformation() 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

111
        $balanceData  = $this->getBalanceInformation($start, /** @scrutinizer ignore-type */ $end);
Loading history...
112
        $billData     = $this->getBillInformation($start, $end);
0 ignored issues
show
It seems like $start can also be of type null; however, parameter $start of FireflyIII\Api\V1\Contro...r::getBillInformation() 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

112
        $billData     = $this->getBillInformation(/** @scrutinizer ignore-type */ $start, $end);
Loading history...
It seems like $end can also be of type null; however, parameter $end of FireflyIII\Api\V1\Contro...r::getBillInformation() 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

112
        $billData     = $this->getBillInformation($start, /** @scrutinizer ignore-type */ $end);
Loading history...
113
        $spentData    = $this->getLeftToSpendInfo($start, $end);
0 ignored issues
show
It seems like $start can also be of type null; however, parameter $start of FireflyIII\Api\V1\Contro...r::getLeftToSpendInfo() 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

113
        $spentData    = $this->getLeftToSpendInfo(/** @scrutinizer ignore-type */ $start, $end);
Loading history...
It seems like $end can also be of type null; however, parameter $end of FireflyIII\Api\V1\Contro...r::getLeftToSpendInfo() 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

113
        $spentData    = $this->getLeftToSpendInfo($start, /** @scrutinizer ignore-type */ $end);
Loading history...
114
        $networthData = $this->getNetWorthInfo($start, $end);
0 ignored issues
show
It seems like $end can also be of type null; however, parameter $end of FireflyIII\Api\V1\Contro...ller::getNetWorthInfo() 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

114
        $networthData = $this->getNetWorthInfo($start, /** @scrutinizer ignore-type */ $end);
Loading history...
It seems like $start can also be of type null; however, parameter $start of FireflyIII\Api\V1\Contro...ller::getNetWorthInfo() 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

114
        $networthData = $this->getNetWorthInfo(/** @scrutinizer ignore-type */ $start, $end);
Loading history...
115
        $total        = array_merge($balanceData, $billData, $spentData, $networthData);
116
117
        // give new keys
118
        $return = [];
119
        foreach ($total as $entry) {
120
            if (null === $code || (null !== $code && $code === $entry['currency_code'])) {
121
                $return[$entry['key']] = $entry;
122
            }
123
        }
124
125
        return response()->json($return);
126
    }
127
128
    /**
129
     * Check if date is outside session range.
130
     *
131
     * @param Carbon $date
132
     *
133
     * @param Carbon $start
134
     * @param Carbon $end
135
     *
136
     * @return bool
137
     */
138
    protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
139
    {
140
        $result = false;
141
        if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
142
            $result = true;
143
        }
144
        // start and end in the past? use $end
145
        if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
146
            $result = true;
147
        }
148
149
        return $result;
150
    }
151
152
    /**
153
     * @param Carbon $start
154
     * @param Carbon $end
155
     *
156
     * @return array
157
     */
158
    private function getBalanceInformation(Carbon $start, Carbon $end): array
159
    {
160
        // prep some arrays:
161
        $incomes  = [];
162
        $expenses = [];
163
        $sums     = [];
164
        $return   = [];
165
166
        // collect income of user using the new group collector.
167
        /** @var GroupCollectorInterface $collector */
168
        $collector = app(GroupCollectorInterface::class);
169
        $collector
170
            ->setRange($start, $end)
171
            // set page to retrieve
172
            ->setPage($this->parameters->get('page'))
173
            // set types of transactions to return.
174
            ->setTypes([TransactionType::DEPOSIT]);
175
176
        $set = $collector->getExtractedJournals();
177
        /** @var array $transactionJournal */
178
        foreach ($set as $transactionJournal) {
179
            $currencyId           = (int) $transactionJournal['currency_id'];
180
            $incomes[$currencyId] = $incomes[$currencyId] ?? '0';
181
            $incomes[$currencyId] = bcadd($incomes[$currencyId], bcmul($transactionJournal['amount'], '-1'));
182
            $sums[$currencyId]    = $sums[$currencyId] ?? '0';
183
            $sums[$currencyId]    = bcadd($sums[$currencyId], bcmul($transactionJournal['amount'], '-1'));
184
        }
185
186
        // collect expenses of user using the new group collector.
187
        /** @var GroupCollectorInterface $collector */
188
        $collector = app(GroupCollectorInterface::class);
189
        $collector
190
            ->setRange($start, $end)
191
            // set page to retrieve
192
            ->setPage($this->parameters->get('page'))
193
            // set types of transactions to return.
194
            ->setTypes([TransactionType::WITHDRAWAL]);
195
196
197
        $set = $collector->getExtractedJournals();
198
199
        /** @var array $transactionJournal */
200
        foreach ($set as $transactionJournal) {
201
            $currencyId            = (int) $transactionJournal['currency_id'];
202
            $expenses[$currencyId] = $expenses[$currencyId] ?? '0';
203
            $expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']);
204
            $sums[$currencyId]     = $sums[$currencyId] ?? '0';
205
            $sums[$currencyId]     = bcadd($sums[$currencyId], $transactionJournal['amount']);
206
        }
207
208
        // format amounts:
209
        $keys = array_keys($sums);
210
        foreach ($keys as $currencyId) {
211
            $currency = $this->currencyRepos->findNull($currencyId);
212
            if (null === $currency) {
213
                continue;
214
            }
215
            // create objects for big array.
216
            $return[] = [
217
                'key'                     => sprintf('balance-in-%s', $currency->code),
218
                'title'                   => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
219
                'monetary_value'          => round($sums[$currencyId] ?? 0, $currency->decimal_places),
220
                'currency_id'             => $currency->id,
221
                'currency_code'           => $currency->code,
222
                'currency_symbol'         => $currency->symbol,
223
                'currency_decimal_places' => $currency->decimal_places,
224
                'value_parsed'            => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false),
225
                'local_icon'              => 'balance-scale',
226
                'sub_title'               => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) .
227
                                             ' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
228
            ];
229
            $return[] = [
230
                'key'                     => sprintf('spent-in-%s', $currency->code),
231
                'title'                   => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
232
                'monetary_value'          => round($expenses[$currencyId] ?? 0, $currency->decimal_places),
233
                'currency_id'             => $currency->id,
234
                'currency_code'           => $currency->code,
235
                'currency_symbol'         => $currency->symbol,
236
                'currency_decimal_places' => $currency->decimal_places,
237
                'value_parsed'            => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false),
238
                'local_icon'              => 'balance-scale',
239
                'sub_title'               => '',
240
            ];
241
            $return[] = [
242
                'key'                     => sprintf('earned-in-%s', $currency->code),
243
                'title'                   => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
244
                'monetary_value'          => round($incomes[$currencyId] ?? 0, $currency->decimal_places),
245
                'currency_id'             => $currency->id,
246
                'currency_code'           => $currency->code,
247
                'currency_symbol'         => $currency->symbol,
248
                'currency_decimal_places' => $currency->decimal_places,
249
                'value_parsed'            => app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
250
                'local_icon'              => 'balance-scale',
251
                'sub_title'               => '',
252
            ];
253
        }
254
255
        return $return;
256
    }
257
258
    /**
259
     * @param Carbon $start
260
     * @param Carbon $end
261
     *
262
     * @return array
263
     */
264
    private function getBillInformation(Carbon $start, Carbon $end): array
265
    {
266
        /*
267
         * Since both this method and the chart use the exact same data, we can suffice
268
         * with calling the one method in the bill repository that will get this amount.
269
         */
270
        $paidAmount   = $this->billRepository->getBillsPaidInRangePerCurrency($start, $end);
271
        $unpaidAmount = $this->billRepository->getBillsUnpaidInRangePerCurrency($start, $end);
272
        $return       = [];
273
        foreach ($paidAmount as $currencyId => $amount) {
274
            $amount   = bcmul($amount, '-1');
275
            $currency = $this->currencyRepos->findNull((int) $currencyId);
276
            if (null === $currency) {
277
                continue;
278
            }
279
            $return[] = [
280
                'key'                     => sprintf('bills-paid-in-%s', $currency->code),
281
                'title'                   => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
282
                'monetary_value'          => 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

282
                'monetary_value'          => round(/** @scrutinizer ignore-type */ $amount, $currency->decimal_places),
Loading history...
283
                'currency_id'             => $currency->id,
284
                'currency_code'           => $currency->code,
285
                'currency_symbol'         => $currency->symbol,
286
                'currency_decimal_places' => $currency->decimal_places,
287
                'value_parsed'            => app('amount')->formatAnything($currency, $amount, false),
288
                'local_icon'              => 'check',
289
                'sub_title'               => '',
290
            ];
291
        }
292
293
        foreach ($unpaidAmount as $currencyId => $amount) {
294
            $amount   = bcmul($amount, '-1');
295
            $currency = $this->currencyRepos->findNull((int) $currencyId);
296
            if (null === $currency) {
297
                continue;
298
            }
299
            $return[] = [
300
                'key'                     => sprintf('bills-unpaid-in-%s', $currency->code),
301
                'title'                   => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
302
                'monetary_value'          => round($amount, $currency->decimal_places),
303
                'currency_id'             => $currency->id,
304
                'currency_code'           => $currency->code,
305
                'currency_symbol'         => $currency->symbol,
306
                'currency_decimal_places' => $currency->decimal_places,
307
                'value_parsed'            => app('amount')->formatAnything($currency, $amount, false),
308
                'local_icon'              => 'calendar-o',
309
                'sub_title'               => '',
310
            ];
311
        }
312
313
        return $return;
314
    }
315
316
    /**
317
     * @param Carbon $start
318
     * @param Carbon $end
319
     *
320
     * @throws Exception
321
     * @return array
322
     */
323
    private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
324
    {
325
        $return    = [];
326
        $today     = new Carbon;
327
        $available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
328
        $budgets   = $this->budgetRepository->getActiveBudgets();
329
        $spent     = $this->opsRepository->sumExpenses($start, $end, null, $budgets);
330
331
        foreach ($spent as $row) {
332
            // either an amount was budgeted or 0 is available.
333
            $amount          = $available[$row['currency_id']] ?? '0';
334
            $spentInCurrency = $row['sum'];
335
            $leftToSpend     = bcadd($amount, $spentInCurrency);
336
337
            $days   = $today->diffInDays($end) + 1;
338
            $perDay = '0';
339
            if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
340
                $perDay = bcdiv($leftToSpend, (string) $days);
341
            }
342
343
            $return[] = [
344
                'key'                     => sprintf('left-to-spend-in-%s', $row['currency_code']),
345
                'title'                   => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
346
                'monetary_value'          => round($leftToSpend, $row['currency_decimal_places']),
0 ignored issues
show
$leftToSpend 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

346
                'monetary_value'          => round(/** @scrutinizer ignore-type */ $leftToSpend, $row['currency_decimal_places']),
Loading history...
347
                'currency_id'             => $row['currency_id'],
348
                'currency_code'           => $row['currency_code'],
349
                'currency_symbol'         => $row['currency_symbol'],
350
                'currency_decimal_places' => $row['currency_decimal_places'],
351
                'value_parsed'            => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $leftToSpend, false),
352
                'local_icon'              => 'money',
353
                'sub_title'               => (string) trans(
354
                    'firefly.box_spend_per_day',
355
                    ['amount' => app('amount')->formatFlat(
356
                        $row['currency_symbol'],
357
                        $row['currency_decimal_places'],
358
                        $perDay,
359
                        false
360
                    )]
361
                ),
362
            ];
363
        }
364
365
        return $return;
366
    }
367
368
    /**
369
     * @param Carbon $start
370
     * @param Carbon $end
371
     *
372
     * @return array
373
     */
374
    private function getNetWorthInfo(Carbon $start, Carbon $end): array
375
    {
376
        /** @var User $user */
377
        $user = auth()->user();
378
        $date = Carbon::now()->startOfDay();
379
380
381
        // start and end in the future? use $end
382
        if ($this->notInDateRange($date, $start, $end)) {
383
            /** @var Carbon $date */
384
            $date = session('end', Carbon::now()->endOfMonth());
385
        }
386
387
        /** @var NetWorthInterface $netWorthHelper */
388
        $netWorthHelper = app(NetWorthInterface::class);
389
        $netWorthHelper->setUser($user);
390
        $allAccounts = $this->accountRepository->getActiveAccountsByType([AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE]);
391
392
        // filter list on preference of being included.
393
        $filtered = $allAccounts->filter(
394
            function (Account $account) {
395
                $includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth');
396
397
                return null === $includeNetWorth ? true : '1' === $includeNetWorth;
398
            }
399
        );
400
401
        $netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date);
402
        $return      = [];
403
        foreach ($netWorthSet as $data) {
404
            /** @var TransactionCurrency $currency */
405
            $currency = $data['currency'];
406
            $amount   = round($data['balance'], $currency->decimal_places);
407
            if (0.0 === $amount) {
408
                continue;
409
            }
410
            // return stuff
411
            $return[] = [
412
                'key'                     => sprintf('net-worth-in-%s', $currency->code),
413
                'title'                   => trans('firefly.box_net_worth_in_currency', ['currency' => $currency->symbol]),
414
                'monetary_value'          => $amount,
415
                'currency_id'             => $currency->id,
416
                'currency_code'           => $currency->code,
417
                'currency_symbol'         => $currency->symbol,
418
                'currency_decimal_places' => $currency->decimal_places,
419
                'value_parsed'            => app('amount')->formatAnything($currency, $data['balance'], false),
420
                'local_icon'              => 'line-chart',
421
                'sub_title'               => '',
422
            ];
423
        }
424
425
        return $return;
426
    }
427
}
428