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

689 lines
34 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
2025-05-24 16:39:20 +02:00
use Exception;
2019-01-18 05:38:23 +01:00
use Carbon\Carbon;
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;
2024-12-24 06:34:12 +01:00
use FireflyIII\Enums\AccountTypeEnum;
2024-11-25 07:14:14 +01:00
use FireflyIII\Enums\TransactionTypeEnum;
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\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;
2025-02-23 12:35:13 +01:00
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
2024-12-24 06:34:12 +01:00
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
2019-01-18 05:38:23 +01:00
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
2025-04-22 20:41:08 +02:00
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
2019-01-18 05:38:23 +01:00
/**
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-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
*
2025-05-24 16:39:20 +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:
$dates = $request->getAll();
$start = $dates['start'];
$end = $dates['end'];
$code = $request->get('currency_code');
2019-01-18 05:38:23 +01:00
// balance information:
$balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getSubscriptionInformation($start, $end);
2019-01-18 05:38:23 +01:00
$spentData = $this->getLeftToSpendInfo($start, $end);
2025-02-16 07:23:03 +01:00
$netWorthData = $this->getNetWorthInfo($end);
// $balanceData = [];
// $billData = [];
// $spentData = [];
// $netWorthData = [];
$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 = [];
2019-08-02 05:49:20 +02:00
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
}
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
Log::debug('getBalanceInformation');
2024-12-24 06:34:12 +01:00
// some config settings
2024-12-28 07:35:20 +01:00
$convertToNative = Amount::convertToNative();
2025-01-19 19:07:19 +01:00
$default = Amount::getNativeCurrency();
2019-01-18 05:38:23 +01:00
// prep some arrays:
$incomes = [];
$expenses = [];
$sums = [];
$return = [];
$currencies = [
$default->id => $default,
];
2019-01-18 05:38:23 +01:00
2019-03-25 15:14:09 +01:00
// collect income of user using the new group collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$summarizer = new TransactionSummarizer();
$set = $collector->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value])->getExtractedJournals();
$incomes = $summarizer->groupByCurrencyId($set, 'positive', false);
2019-01-18 05:38:23 +01:00
// collect expenses of user.
// collect expenses of user using the new group collector.
2019-03-25 15:14:09 +01:00
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$set = $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->getExtractedJournals();
$expenses = $summarizer->groupByCurrencyId($set, 'negative', false);
// if convert to native, do so right now.
if ($convertToNative) {
$newExpenses = [
$default->id => [
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'sum' => '0',
],
];
2025-03-23 15:19:40 +01:00
$newIncomes = [
$default->id => [
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'sum' => '0',
],
];
2025-03-23 15:19:40 +01:00
$sums = [
$default->id => [
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'sum' => '0',
],
];
2019-01-18 05:38:23 +01:00
$converter = new ExchangeRateConverter();
// loop over income and expenses
2025-03-23 15:19:40 +01:00
foreach ([$expenses, $incomes] as $index => $array) {
// loop over either one.
foreach ($array as $entry) {
// if it is the native currency already.
if ($entry['currency_id'] === $default->id) {
2025-05-04 13:50:20 +02:00
$sums[$default->id]['sum'] = bcadd((string) $entry['sum'], $sums[$default->id]['sum']);
// don't forget to add it to newExpenses and newIncome
2025-03-23 15:19:40 +01:00
if (0 === $index) {
2025-05-04 13:50:20 +02:00
$newExpenses[$default->id]['sum'] = bcadd($newExpenses[$default->id]['sum'], (string) $entry['sum']);
}
2025-03-23 15:19:40 +01:00
if (1 === $index) {
2025-05-04 13:50:20 +02:00
$newIncomes[$default->id]['sum'] = bcadd($newIncomes[$default->id]['sum'], (string) $entry['sum']);
}
continue;
}
$currencies[$entry['currency_id']] ??= $this->currencyRepos->find($entry['currency_id']);
$convertedSum = $converter->convert($currencies[$entry['currency_id']], $default, $start, $entry['sum']);
$sums[$default->id]['sum'] = bcadd($sums[$default->id]['sum'], $convertedSum);
2025-03-23 15:19:40 +01:00
if (0 === $index) {
$newExpenses[$default->id]['sum'] = bcadd($newExpenses[$default->id]['sum'], $convertedSum);
}
2025-03-23 15:19:40 +01:00
if (1 === $index) {
$newIncomes[$default->id]['sum'] = bcadd($newIncomes[$default->id]['sum'], $convertedSum);
}
}
}
$incomes = $newIncomes;
$expenses = $newExpenses;
}
2025-03-23 15:19:40 +01:00
if (!$convertToNative) {
foreach ([$expenses, $incomes] as $array) {
foreach ($array as $entry) {
2025-03-23 15:19:40 +01:00
$currencyId = $entry['currency_id'];
$sums[$currencyId] ??= [
'currency_id' => $entry['currency_id'],
'currency_code' => $entry['currency_code'],
'currency_symbol' => $entry['currency_symbol'],
'currency_decimal_places' => $entry['currency_decimal_places'],
'sum' => '0',
];
2025-05-04 13:50:20 +02:00
$sums[$currencyId]['sum'] = bcadd($sums[$currencyId]['sum'], (string) $entry['sum']);
}
}
}
2019-01-18 05:38:23 +01:00
// format amounts:
$keys = array_keys($sums);
2019-01-18 05:38:23 +01:00
foreach ($keys as $currencyId) {
$currency = $currencies[$currencyId] ?? $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]),
'monetary_value' => $sums[$currencyId]['sum'] ?? '0',
2024-12-22 08:43:12 +01:00
'currency_id' => (string) $currency->id,
2019-01-18 05:38:23 +01:00
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId]['sum'] ?? '0', false),
2019-01-18 05:38:23 +01:00
'local_icon' => 'balance-scale',
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false)
.' + '.app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '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]),
'monetary_value' => $expenses[$currencyId]['sum'] ?? '0',
2024-12-22 08:43:12 +01:00
'currency_id' => (string) $currency->id,
2019-01-18 05:38:23 +01:00
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false),
2019-01-18 05:38:23 +01:00
'local_icon' => 'balance-scale',
'sub_title' => '',
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $incomes[$currencyId]['sum'] ?? '0',
2024-12-22 08:43:12 +01:00
'currency_id' => (string) $currency->id,
2019-01-18 05:38:23 +01:00
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false),
2019-01-18 05:38:23 +01:00
'local_icon' => 'balance-scale',
'sub_title' => '',
];
}
if (0 === count($return)) {
2025-03-05 14:58:10 +01:00
$currency = $this->nativeCurrency;
// create objects for big array.
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, '0', false),
'local_icon' => 'balance-scale',
'sub_title' => app('amount')->formatAnything($currency, '0', false)
.' + '.app('amount')->formatAnything($currency, '0', false),
2025-03-05 14:58:10 +01:00
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, '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]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, '0', false),
'local_icon' => 'balance-scale',
'sub_title' => '',
];
}
2019-01-18 05:38:23 +01:00
return $return;
}
private function getSubscriptionInformation(Carbon $start, Carbon $end): array
2019-01-18 05:38:23 +01:00
{
Log::debug(sprintf('Now in getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-')));
2019-01-18 05:38:23 +01:00
/*
* 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.
*/
2023-06-01 19:49:28 +02:00
$paidAmount = $this->billRepository->sumPaidInRange($start, $end);
$unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end);
2025-03-23 15:19:40 +01:00
$currencies = [
$this->nativeCurrency->id => $this->nativeCurrency,
];
2025-03-23 15:19:40 +01:00
if ($this->convertToNative) {
$converter = new ExchangeRateConverter();
$newPaidAmount = [[
'id' => $this->nativeCurrency->id,
'name' => $this->nativeCurrency->name,
'symbol' => $this->nativeCurrency->symbol,
'code' => $this->nativeCurrency->code,
'decimal_places' => $this->nativeCurrency->decimal_places,
'sum' => '0',
]];
$newUnpaidAmount = [[
'id' => $this->nativeCurrency->id,
'name' => $this->nativeCurrency->name,
'symbol' => $this->nativeCurrency->symbol,
'code' => $this->nativeCurrency->code,
'decimal_places' => $this->nativeCurrency->decimal_places,
'sum' => '0',
]];
2025-03-23 15:19:40 +01:00
foreach ([$paidAmount, $unpaidAmount] as $index => $array) {
foreach ($array as $item) {
$currencyId = (int) $item['id'];
2025-03-23 15:19:40 +01:00
if (0 === $index) {
// paid amount
2025-03-23 15:19:40 +01:00
if ($currencyId === $this->nativeCurrency->id) {
2025-05-04 13:50:20 +02:00
$newPaidAmount[0]['sum'] = bcadd($newPaidAmount[0]['sum'], (string) $item['sum']);
continue;
}
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
2025-03-23 15:19:40 +01:00
$convertedAmount = $converter->convert($currencies[$currencyId], $this->nativeCurrency, $start, $item['sum']);
$newPaidAmount[0]['sum'] = bcadd($newPaidAmount[0]['sum'], $convertedAmount);
continue;
}
// unpaid amount
2025-03-23 15:19:40 +01:00
if ($currencyId === $this->nativeCurrency->id) {
2025-05-04 13:50:20 +02:00
$newUnpaidAmount[0]['sum'] = bcadd($newUnpaidAmount[0]['sum'], (string) $item['sum']);
continue;
}
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
2025-03-23 15:19:40 +01:00
$convertedAmount = $converter->convert($currencies[$currencyId], $this->nativeCurrency, $start, $item['sum']);
$newUnpaidAmount[0]['sum'] = bcadd($newUnpaidAmount[0]['sum'], $convertedAmount);
}
}
$paidAmount = $newPaidAmount;
$unpaidAmount = $newUnpaidAmount;
}
// var_dump($paidAmount);
// var_dump($unpaidAmount);
// exit;
2023-06-01 19:49:28 +02:00
$return = [];
2023-12-20 19:35:52 +01:00
2023-06-01 19:49:28 +02:00
/**
* @var array $info
*/
foreach ($paidAmount as $info) {
2025-05-04 13:50:20 +02:00
$amount = bcmul((string) $info['sum'], '-1');
2019-01-18 05:38:23 +01:00
$return[] = [
'key' => sprintf('bills-paid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $info['symbol']]),
2022-12-29 19:41:57 +01:00
'monetary_value' => $amount,
2024-12-22 08:43:12 +01:00
'currency_id' => (string) $info['id'],
'currency_code' => $info['code'],
'currency_symbol' => $info['symbol'],
'currency_decimal_places' => $info['decimal_places'],
'value_parsed' => app('amount')->formatFlat($info['symbol'], $info['decimal_places'], $amount, false),
2019-01-18 05:38:23 +01:00
'local_icon' => 'check',
'sub_title' => '',
];
}
2023-06-01 19:49:28 +02:00
/**
* @var array $info
*/
foreach ($unpaidAmount as $info) {
2025-05-04 13:50:20 +02:00
$amount = bcmul((string) $info['sum'], '-1');
2019-01-18 05:38:23 +01:00
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $info['code']),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $info['symbol']]),
2022-12-24 05:06:39 +01:00
'monetary_value' => $amount,
2024-12-22 08:43:12 +01:00
'currency_id' => (string) $info['id'],
'currency_code' => $info['code'],
'currency_symbol' => $info['symbol'],
'currency_decimal_places' => $info['decimal_places'],
'value_parsed' => app('amount')->formatFlat($info['symbol'], $info['decimal_places'], $amount, false),
2019-01-18 05:38:23 +01:00
'local_icon' => 'calendar-o',
'sub_title' => '',
];
}
Log::debug(sprintf('Done with getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-')));
2023-12-20 19:35:52 +01:00
if (0 === count($return)) {
2025-03-05 14:58:10 +01:00
$currency = $this->nativeCurrency;
unset($info, $amount);
2025-03-05 14:58:10 +01:00
$return[] = [
'key' => sprintf('bills-paid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
'local_icon' => 'check',
'sub_title' => '',
];
$return[] = [
'key' => sprintf('bills-unpaid-in-%s', $currency->code),
'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
'local_icon' => 'calendar-o',
'sub_title' => '',
];
}
2019-01-18 05:38:23 +01:00
return $return;
}
/**
2025-05-24 16:39:20 +02:00
* @throws Exception
2019-01-18 05:38:23 +01:00
*/
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{
2025-04-11 19:38:48 +02:00
Log::debug(sprintf('Now in getLeftToSpendInfo("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
$return = [];
$today = today(config('app.timezone'));
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
$budgets = $this->budgetRepository->getActiveBudgets();
$spent = $this->opsRepository->sumExpenses($start, $end, null, $budgets);
$days = (int) $today->diffInDays($end, true) + 1;
2025-04-11 19:38:48 +02:00
$currencies = [];
2025-04-11 19:38:48 +02:00
// first, create an entry for each entry in the "available" array.
/** @var array $availableBudget */
2025-05-04 17:41:26 +02:00
foreach ($available as $currencyId => $availableBudget) {
2025-04-11 19:38:48 +02:00
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
$return[$currencyId] = [
2025-04-11 19:38:48 +02:00
'key' => sprintf('left-to-spend-in-%s', $currencies[$currencyId]->code),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currencies[$currencyId]->symbol]),
'no_available_budgets' => false,
2025-04-11 19:38:48 +02:00
'monetary_value' => $availableBudget,
'currency_id' => (string) $currencies[$currencyId]->id,
'currency_code' => $currencies[$currencyId]->code,
'currency_symbol' => $currencies[$currencyId]->symbol,
'currency_decimal_places' => $currencies[$currencyId]->decimal_places,
'value_parsed' => app('amount')->formatFlat($currencies[$currencyId]->symbol, $currencies[$currencyId]->decimal_places, $availableBudget, false),
'local_icon' => 'money',
'sub_title' => app('amount')->formatFlat(
$currencies[$currencyId]->symbol,
$currencies[$currencyId]->decimal_places,
$availableBudget,
false
),
];
}
2019-09-20 16:33:15 +02:00
foreach ($spent as $row) {
// either an amount was budgeted or 0 is available.
$currencyId = (int) $row['currency_id'];
$amount = (string) ($available[$currencyId] ?? '0');
if (0 === bccomp($amount, '0')) {
2025-04-07 19:44:22 +02:00
// #9858 skip over currencies with no available budget.
continue;
}
$spentInCurrency = $row['sum'];
2025-05-04 13:50:20 +02:00
$leftToSpend = bcadd($amount, (string) $spentInCurrency);
$perDay = '0';
2019-01-18 05:38:23 +01:00
if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
2024-12-22 08:43:12 +01:00
$perDay = bcdiv($leftToSpend, (string) $days);
2019-01-18 05:38:23 +01:00
}
Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum']));
$return[$currencyId] = [
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']]),
'no_available_budgets' => false,
2022-12-29 19:41:57 +01:00
'monetary_value' => $leftToSpend,
2024-12-22 08:43:12 +01:00
'currency_id' => (string) $row['currency_id'],
2019-09-20 16:33:15 +02:00
'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',
2023-07-23 07:10:31 +02:00
'sub_title' => 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
}
2025-04-22 20:41:08 +02:00
unset($leftToSpend);
if (0 === count($return)) {
2025-04-22 20:41:08 +02:00
// a small trick to get every expense in this period, regardless of budget.
$spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection());
2025-04-22 20:41:08 +02:00
foreach ($spent as $row) {
// either an amount was budgeted or 0 is available.
$currencyId = (int) $row['currency_id'];
$spentInCurrency = $row['sum'];
$perDay = '0';
2025-05-04 13:50:20 +02:00
if (0 !== $days && -1 === bccomp((string) $spentInCurrency, '0')) {
$perDay = bcdiv((string) $spentInCurrency, (string) $days);
2025-04-22 20:41:08 +02:00
}
Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum']));
$return[$currencyId] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'title' => trans('firefly.spent'),
'no_available_budgets' => true,
'monetary_value' => $spentInCurrency,
'currency_id' => (string) $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'], $spentInCurrency, false),
'local_icon' => 'money',
'sub_title' => app('amount')->formatFlat(
$row['currency_symbol'],
$row['currency_decimal_places'],
$perDay,
false
),
];
}
// $amount = '0';
// // $days
// // fill in by money spent, just count it.
// $currency = $this->nativeCurrency;
// $return[$currency->id] = [
// 'key' => sprintf('left-to-spend-in-%s', $currency->code),
// 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currency->symbol]),
// 'monetary_value' => '0',
// 'no_available_budgets' => true,
// 'currency_id' => (string) $currency->id,
// 'currency_code' => $currency->code,
// 'currency_symbol' => $currency->symbol,
// 'currency_decimal_places' => $currency->decimal_places,
// 'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
// 'local_icon' => 'money',
// 'sub_title' => app('amount')->formatFlat(
// $currency->symbol,
// $currency->decimal_places,
// '0',
// false
// ),
// ];
2025-03-05 14:58:10 +01:00
}
2025-04-11 19:38:48 +02:00
return array_values($return);
2019-01-18 05:38:23 +01:00
}
2025-02-16 07:23:03 +01:00
private function getNetWorthInfo(Carbon $end): array
2019-01-18 05:38:23 +01:00
{
2025-02-16 07:23:03 +01:00
$end->endOfDay();
2019-02-13 17:38:41 +01:00
/** @var User $user */
$user = auth()->user();
2025-02-16 07:23:03 +01:00
Log::debug(sprintf('getNetWorthInfo up until "%s".', $end->format('Y-m-d H:i:s')));
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);
$allAccounts = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]);
2019-01-18 05:38:23 +01:00
// filter list on preference of being included.
$filtered = $allAccounts->filter(
2019-01-18 05:38:23 +01:00
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->byAccounts($filtered, $end);
$return = [];
2023-10-29 05:54:01 +01:00
foreach ($netWorthSet as $key => $data) {
2023-10-29 06:36:37 +01:00
if ('native' === $key) {
2023-10-29 05:54:01 +01:00
continue;
}
$amount = $data['balance'];
2025-05-04 13:50:20 +02:00
if (0 === bccomp((string) $amount, '0')) {
2019-01-18 05:38:23 +01:00
continue;
}
// return stuff
$return[] = [
2023-10-29 05:54:01 +01:00
'key' => sprintf('net-worth-in-%s', $data['currency_code']),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $data['currency_symbol']]),
2019-01-18 05:38:23 +01:00
'monetary_value' => $amount,
2024-12-22 08:43:12 +01:00
'currency_id' => (string) $data['currency_id'],
2023-10-29 05:54:01 +01:00
'currency_code' => $data['currency_code'],
'currency_symbol' => $data['currency_symbol'],
'currency_decimal_places' => $data['currency_decimal_places'],
'value_parsed' => app('amount')->formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false),
2019-01-18 05:38:23 +01:00
'local_icon' => 'line-chart',
'sub_title' => '',
];
}
if (0 === count($return)) {
2025-02-16 06:33:53 +01:00
$return[] = [
'key' => sprintf('net-worth-in-%s', $this->nativeCurrency->code),
'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $this->nativeCurrency->symbol]),
'monetary_value' => '0',
'currency_id' => (string) $this->nativeCurrency->id,
'currency_code' => $this->nativeCurrency->code,
'currency_symbol' => $this->nativeCurrency->symbol,
'currency_decimal_places' => $this->nativeCurrency->decimal_places,
'value_parsed' => app('amount')->formatFlat($this->nativeCurrency->symbol, $this->nativeCurrency->decimal_places, '0', false),
'local_icon' => 'line-chart',
'sub_title' => '',
];
}
Log::debug('End of getNetWorthInfo');
2023-06-21 12:34:58 +02:00
2023-12-20 19:35:52 +01:00
return $return;
2023-06-21 12:34:58 +02:00
}
/**
* Check if date is outside session range.
*/
protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference
{
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
2025-05-24 16:39:20 +02:00
return true;
}
// start and end in the past? use $end
if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) {
2025-05-24 16:39:20 +02:00
return true;
}
2025-05-24 16:39:20 +02:00
return false;
}
2019-02-09 10:36:59 +01:00
}