Files
firefly-iii/app/Api/V1/Controllers/Summary/BasicController.php

427 lines
18 KiB
PHP
Raw Normal View History

2019-01-18 05:38:23 +01:00
<?php
2019-02-09 10:36:59 +01:00
2021-03-03 07:02:57 +01:00
/*
2019-02-09 10:36:59 +01:00
* SummaryController.php
2021-03-03 07:02:57 +01:00
* Copyright (c) 2021 james@firefly-iii.org
2019-02-09 10:36:59 +01:00
*
* This file is part of Firefly III (https://github.com/firefly-iii).
2019-02-09 10:36:59 +01:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
2019-02-09 10:36:59 +01:00
*
* This program is distributed in the hope that it will be useful,
2019-02-09 10:36:59 +01:00
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
2019-02-09 10:36:59 +01:00
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
2019-02-09 10:36:59 +01:00
*/
2019-01-18 05:38:23 +01:00
declare(strict_types=1);
2021-03-03 07:02:57 +01:00
namespace FireflyIII\Api\V1\Controllers\Summary;
2019-01-18 05:38:23 +01:00
use Carbon\Carbon;
2019-03-30 07:09:52 +01:00
use Exception;
2021-03-03 07:02:57 +01:00
use FireflyIII\Api\V1\Controllers\Controller;
2021-03-07 12:16:03 +01:00
use FireflyIII\Api\V1\Requests\Data\DateRequest;
2019-03-25 15:14:09 +01:00
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
2019-01-18 05:38:23 +01:00
use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
2019-08-30 08:00:52 +02:00
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
2019-01-18 05:38:23 +01:00
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
2019-08-30 08:19:55 +02:00
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
2019-01-18 05:38:23 +01:00
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
2021-03-03 07:02:57 +01:00
* Class BasicController
2019-01-18 05:38:23 +01:00
*/
2021-03-03 07:02:57 +01:00
class BasicController extends Controller
2019-01-18 05:38:23 +01:00
{
private AvailableBudgetRepositoryInterface $abRepository;
2021-03-21 09:15:40 +01:00
private AccountRepositoryInterface $accountRepository;
private BillRepositoryInterface $billRepository;
private BudgetRepositoryInterface $budgetRepository;
private CurrencyRepositoryInterface $currencyRepos;
private OperationsRepositoryInterface $opsRepository;
2020-07-31 09:24:08 +02:00
2019-01-18 05:38:23 +01:00
/**
2021-03-03 07:02:57 +01:00
* BasicController constructor.
2019-08-30 08:00:52 +02:00
*
2019-01-18 05:38:23 +01:00
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->billRepository = app(BillRepositoryInterface::class);
$this->budgetRepository = app(BudgetRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
2019-08-30 08:00:52 +02:00
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
2019-09-04 17:39:39 +02:00
$this->opsRepository = app(OperationsRepositoryInterface::class);
2019-01-18 05:38:23 +01:00
$this->billRepository->setUser($user);
$this->currencyRepos->setUser($user);
$this->budgetRepository->setUser($user);
$this->accountRepository->setUser($user);
2019-08-30 08:00:52 +02:00
$this->abRepository->setUser($user);
2019-08-30 08:19:55 +02:00
$this->opsRepository->setUser($user);
2019-01-18 05:38:23 +01:00
return $next($request);
}
);
}
/**
2021-09-19 17:22:16 +02:00
* This endpoint is documented at:
2023-02-12 06:53:36 +01:00
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/summary/getBasicSummary
2021-09-19 17:22:16 +02:00
*
2022-12-29 19:41:57 +01:00
* @param DateRequest $request
2019-01-18 05:38:23 +01:00
*
* @return JsonResponse
2020-10-18 08:01:10 +02:00
* @throws Exception
2019-01-18 05:38:23 +01:00
*/
2019-06-09 15:28:54 +02:00
public function basic(DateRequest $request): JsonResponse
2019-01-18 05:38:23 +01:00
{
// parameters for boxes:
2019-06-09 15:28:54 +02:00
$dates = $request->getAll();
$start = $dates['start'];
$end = $dates['end'];
2019-08-02 05:49:20 +02:00
$code = $request->get('currency_code');
2019-06-09 15:28:54 +02:00
2019-01-18 05:38:23 +01:00
// balance information:
$balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end);
$networthData = $this->getNetWorthInfo($start, $end);
$total = array_merge($balanceData, $billData, $spentData, $networthData);
2019-02-13 17:38:41 +01:00
2019-08-02 05:49:20 +02:00
// give new keys
$return = [];
foreach ($total as $entry) {
2022-03-29 15:10:05 +02:00
if (null === $code || ($code === $entry['currency_code'])) {
2019-08-02 05:49:20 +02:00
$return[$entry['key']] = $entry;
}
}
2019-01-18 05:38:23 +01:00
2019-08-02 05:49:20 +02:00
return response()->json($return);
2019-01-18 05:38:23 +01:00
}
2023-05-29 13:56:55 +02:00
/**
* Check if date is outside session range.
*
* @param Carbon $date
*
* @param Carbon $start
* @param Carbon $end
*
* @return bool
*/
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
{
$result = false;
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
$result = true;
}
// start and end in the past? use $end
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
$result = true;
}
return $result;
}
2019-01-18 05:38:23 +01:00
/**
2022-12-29 19:41:57 +01:00
* @param Carbon $start
* @param Carbon $end
2019-01-18 05:38:23 +01:00
*
* @return array
*/
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
// prep some arrays:
$incomes = [];
$expenses = [];
$sums = [];
$return = [];
2019-03-25 15:14:09 +01:00
// collect income of user using the new group collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setRange($start, $end)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes([TransactionType::DEPOSIT]);
2019-03-30 07:09:52 +01:00
$set = $collector->getExtractedJournals();
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
2022-12-29 19:41:57 +01:00
$currencyId = (int)$transactionJournal['currency_id'];
2019-03-30 07:09:52 +01:00
$incomes[$currencyId] = $incomes[$currencyId] ?? '0';
2022-10-30 14:23:00 +01:00
$incomes[$currencyId] = bcadd(
$incomes[$currencyId],
bcmul($transactionJournal['amount'], '-1')
2022-03-28 12:24:16 +02:00
);
2019-03-30 07:09:52 +01:00
$sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums[$currencyId] = bcadd($sums[$currencyId], bcmul($transactionJournal['amount'], '-1'));
2019-01-18 05:38:23 +01:00
}
2019-03-25 15:14:09 +01:00
// collect expenses of user using the new group collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setRange($start, $end)
// set page to retrieve
->setPage($this->parameters->get('page'))
// set types of transactions to return.
->setTypes([TransactionType::WITHDRAWAL]);
2019-03-30 07:09:52 +01:00
$set = $collector->getExtractedJournals();
2019-03-25 15:14:09 +01:00
2019-03-30 07:09:52 +01:00
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
2022-12-29 19:41:57 +01:00
$currencyId = (int)$transactionJournal['currency_id'];
2019-03-30 07:09:52 +01:00
$expenses[$currencyId] = $expenses[$currencyId] ?? '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']);
$sums[$currencyId] = $sums[$currencyId] ?? '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $transactionJournal['amount']);
2019-01-18 05:38:23 +01:00
}
// format amounts:
$keys = array_keys($sums);
foreach ($keys as $currencyId) {
2021-06-30 06:17:38 +02:00
$currency = $this->currencyRepos->find($currencyId);
2019-01-18 05:38:23 +01:00
if (null === $currency) {
continue;
}
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
2022-12-29 19:41:57 +01:00
'monetary_value' => $sums[$currencyId] ?? '0',
2019-01-18 05:38:23 +01:00
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false),
'local_icon' => 'balance-scale',
2022-12-29 19:41:57 +01:00
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false).
' + '.app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
2019-01-18 05:38:23 +01:00
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
2022-12-24 05:06:39 +01:00
'monetary_value' => $expenses[$currencyId] ?? '0',
2019-01-18 05:38:23 +01:00
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => '',
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
2022-12-24 05:06:39 +01:00
'monetary_value' => $incomes[$currencyId] ?? '0',
2019-01-18 05:38:23 +01:00
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => '',
];
}
return $return;
}
/**
2022-12-29 19:41:57 +01:00
* @param Carbon $start
* @param Carbon $end
2019-01-18 05:38:23 +01:00
*
* @return array
*/
private function getBillInformation(Carbon $start, Carbon $end): array
{
/*
* Since both this method and the chart use the exact same data, we can suffice
* with calling the one method in the bill repository that will get this amount.
*/
$paidAmount = $this->billRepository->getBillsPaidInRangePerCurrency($start, $end);
$unpaidAmount = $this->billRepository->getBillsUnpaidInRangePerCurrency($start, $end);
$return = [];
foreach ($paidAmount as $currencyId => $amount) {
$amount = bcmul($amount, '-1');
2022-12-29 19:41:57 +01:00
$currency = $this->currencyRepos->find((int)$currencyId);
2019-01-18 05:38:23 +01:00
if (null === $currency) {
continue;
}
$return[] = [
'key' => sprintf('bills-paid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
2022-12-29 19:41:57 +01:00
'monetary_value' => $amount,
2019-01-18 05:38:23 +01:00
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $amount, false),
'local_icon' => 'check',
'sub_title' => '',
];
}
foreach ($unpaidAmount as $currencyId => $amount) {
$amount = bcmul($amount, '-1');
2022-12-29 19:41:57 +01:00
$currency = $this->currencyRepos->find((int)$currencyId);
2019-01-18 05:38:23 +01:00
if (null === $currency) {
continue;
}
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
2022-12-24 05:06:39 +01:00
'monetary_value' => $amount,
2019-01-18 05:38:23 +01:00
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $amount, false),
'local_icon' => 'calendar-o',
'sub_title' => '',
];
}
return $return;
}
/**
2022-12-29 19:41:57 +01:00
* @param Carbon $start
* @param Carbon $end
2019-01-18 05:38:23 +01:00
*
* @return array
2020-10-18 08:01:10 +02:00
* @throws Exception
2019-01-18 05:38:23 +01:00
*/
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{
$return = [];
2020-09-11 07:12:33 +02:00
$today = today(config('app.timezone'));
2019-08-30 08:00:52 +02:00
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
2019-01-18 05:38:23 +01:00
$budgets = $this->budgetRepository->getActiveBudgets();
2019-09-20 16:33:15 +02:00
$spent = $this->opsRepository->sumExpenses($start, $end, null, $budgets);
foreach ($spent as $row) {
// either an amount was budgeted or 0 is available.
$amount = $available[$row['currency_id']] ?? '0';
$spentInCurrency = $row['sum'];
2019-01-18 05:38:23 +01:00
$leftToSpend = bcadd($amount, $spentInCurrency);
$days = $today->diffInDays($end) + 1;
$perDay = '0';
if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
2022-12-29 19:41:57 +01:00
$perDay = bcdiv($leftToSpend, (string)$days);
2019-01-18 05:38:23 +01:00
}
$return[] = [
2019-09-20 16:33:15 +02:00
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
2022-12-29 19:41:57 +01:00
'monetary_value' => $leftToSpend,
2019-09-20 16:33:15 +02:00
'currency_id' => $row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => $row['currency_decimal_places'],
'value_parsed' => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $leftToSpend, false),
2019-01-18 05:38:23 +01:00
'local_icon' => 'money',
2022-12-29 19:41:57 +01:00
'sub_title' => (string)trans(
2020-03-15 08:16:16 +01:00
'firefly.box_spend_per_day',
2022-12-29 19:41:57 +01:00
[
'amount' => app('amount')->formatFlat(
$row['currency_symbol'],
$row['currency_decimal_places'],
$perDay,
false
),
]
2019-09-20 16:33:15 +02:00
),
2019-01-18 05:38:23 +01:00
];
2019-09-20 16:33:15 +02:00
}
2019-01-18 05:38:23 +01:00
return $return;
}
/**
2022-12-29 19:41:57 +01:00
* @param Carbon $start
* @param Carbon $end
2019-01-18 05:38:23 +01:00
*
* @return array
*/
private function getNetWorthInfo(Carbon $start, Carbon $end): array
{
2019-02-13 17:38:41 +01:00
/** @var User $user */
$user = auth()->user();
2023-02-11 07:36:45 +01:00
$date = today(config('app.timezone'))->startOfDay();
2019-01-18 05:38:23 +01:00
// start and end in the future? use $end
if ($this->notInDateRange($date, $start, $end)) {
/** @var Carbon $date */
2023-02-11 07:36:45 +01:00
$date = session('end', today(config('app.timezone'))->endOfMonth());
2019-01-18 05:38:23 +01:00
}
2023-02-11 07:39:00 +01:00
2019-01-18 05:38:23 +01:00
/** @var NetWorthInterface $netWorthHelper */
$netWorthHelper = app(NetWorthInterface::class);
2019-02-13 17:38:41 +01:00
$netWorthHelper->setUser($user);
2023-02-22 18:14:14 +01:00
$allAccounts = $this->accountRepository->getActiveAccountsByType(
[AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT]
);
2019-01-18 05:38:23 +01:00
// filter list on preference of being included.
$filtered = $allAccounts->filter(
function (Account $account) {
$includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth');
2022-03-30 20:09:19 +02:00
return null === $includeNetWorth || '1' === $includeNetWorth;
2019-01-18 05:38:23 +01:00
}
);
$netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date);
$return = [];
2019-06-09 15:28:54 +02:00
foreach ($netWorthSet as $data) {
2019-01-18 05:38:23 +01:00
/** @var TransactionCurrency $currency */
$currency = $data['currency'];
2022-12-29 19:41:57 +01:00
$amount = $data['balance'];
2022-12-24 05:06:39 +01:00
if (0 === bccomp($amount, '0')) {
2019-01-18 05:38:23 +01:00
continue;
}
// return stuff
$return[] = [
'key' => sprintf('net-worth-in-%s', $currency->code),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $amount,
'currency_id' => $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $data['balance'], false),
'local_icon' => 'line-chart',
'sub_title' => '',
];
}
return $return;
}
2019-02-09 10:36:59 +01:00
}