mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Simplify chart code with external helper.
This commit is contained in:
parent
0bf0b762b8
commit
cd151da240
@ -36,6 +36,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\Support\Chart\Budget\FrontpageChartGenerator;
|
||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@ -411,6 +412,7 @@ class BudgetController extends Controller
|
||||
{
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
@ -419,59 +421,14 @@ class BudgetController extends Controller
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$budgets = $this->repository->getActiveBudgets();
|
||||
$chartData = [
|
||||
['label' => (string) trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string) trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string) trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'],
|
||||
];
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
|
||||
if (0 === $limits->count()) {
|
||||
$spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]), null);
|
||||
/** @var array $entry */
|
||||
foreach ($spent as $entry) {
|
||||
$title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
|
||||
$chartData[0]['entries'][$title] = bcmul($entry['sum'], '-1'); // spent
|
||||
$chartData[1]['entries'][$title] = 0; // left to spend
|
||||
$chartData[2]['entries'][$title] = 0; // overspent
|
||||
}
|
||||
}
|
||||
if (0 !== $limits->count()) {
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($limits as $limit) {
|
||||
$spent = $this->opsRepository->sumExpenses(
|
||||
$limit->start_date,
|
||||
$limit->end_date,
|
||||
null,
|
||||
new Collection([$budget]),
|
||||
$limit->transactionCurrency
|
||||
);
|
||||
/** @var array $entry */
|
||||
foreach ($spent as $entry) {
|
||||
$title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
|
||||
if ($limit->start_date->startOfDay()->ne($start->startOfDay()) || $limit->end_date->startOfDay()->ne($end->startOfDay())) {
|
||||
$title = sprintf(
|
||||
'%s (%s) (%s - %s)',
|
||||
$budget->name,
|
||||
$entry['currency_name'],
|
||||
$limit->start_date->formatLocalized($this->monthAndDayFormat),
|
||||
$limit->end_date->formatLocalized($this->monthAndDayFormat)
|
||||
);
|
||||
}
|
||||
$sumSpent = bcmul($entry['sum'], '-1'); // spent
|
||||
$chartData[0]['entries'][$title] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent;
|
||||
$chartData[1]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($entry['sum'], $limit->amount)
|
||||
: '0';
|
||||
$chartData[2]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ?
|
||||
'0' : bcmul(bcadd($entry['sum'], $limit->amount), '-1');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$generator = app(FrontpageChartGenerator::class);
|
||||
$generator->setUser(auth()->user());
|
||||
$generator->setStart($start);
|
||||
$generator->setEnd($end);
|
||||
|
||||
$chartData = $generator->generate();
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return response()->json($data);
|
||||
|
229
app/Support/Chart/Budget/FrontpageChartGenerator.php
Normal file
229
app/Support/Chart/Budget/FrontpageChartGenerator.php
Normal file
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
/**
|
||||
* FrontpageChartGenerator.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* 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.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Chart\Budget;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class FrontpageChartGenerator
|
||||
*/
|
||||
class FrontpageChartGenerator
|
||||
{
|
||||
private User $user;
|
||||
private Carbon $start;
|
||||
private Carbon $end;
|
||||
private BudgetRepositoryInterface $budgetRepository;
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
protected OperationsRepositoryInterface $opsRepository;
|
||||
private string $monthAndDayFormat;
|
||||
|
||||
/**
|
||||
* FrontpageChartGenerator constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->monthAndDayFormat = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the data for a budget chart. Collect all budgets and process each budget.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function generate(): array
|
||||
{
|
||||
$budgets = $this->budgetRepository->getActiveBudgets();
|
||||
$data = [
|
||||
['label' => (string) trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string) trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string) trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'],
|
||||
];
|
||||
|
||||
// loop al budgets:
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
$data = $this->processBudget($data, $budget);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic setter for the user. Also updates the repositories with the right user.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->budgetRepository->setUser($user);
|
||||
$this->blRepository->setUser($user);
|
||||
$this->opsRepository->setUser($user);
|
||||
|
||||
$locale = app('steam')->getLocale();
|
||||
$this->monthAndDayFormat = (string) trans('config.month_and_day', [], $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
*/
|
||||
public function setStart(Carbon $start): void
|
||||
{
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $end
|
||||
*/
|
||||
public function setEnd(Carbon $end): void
|
||||
{
|
||||
$this->end = $end;
|
||||
}
|
||||
|
||||
/**
|
||||
* For each budget, gets all budget limits for the current time range.
|
||||
* When no limits are present, the time range is used to collect information on money spent.
|
||||
* If limits are present, each limit is processed individually.
|
||||
*
|
||||
* @param array $data
|
||||
* @param Budget $budget
|
||||
* @return array
|
||||
*/
|
||||
private function processBudget(array $data, Budget $budget): array
|
||||
{
|
||||
// get all limits:
|
||||
$limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end);
|
||||
|
||||
// if no limits
|
||||
if (0 === $limits->count()) {
|
||||
return $this->noBudgetLimits($data, $budget);
|
||||
}
|
||||
|
||||
// if limits:
|
||||
if (0 !== $limits->count()) {
|
||||
return $this->budgetLimits($data, $budget, $limits);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* When no limits are present, the expenses of the whole period are collected and grouped.
|
||||
* This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty.
|
||||
*
|
||||
* @param array $data
|
||||
* @param Budget $budget
|
||||
* @return array
|
||||
*/
|
||||
private function noBudgetLimits(array $data, Budget $budget): array
|
||||
{
|
||||
$spent = $this->opsRepository->sumExpenses($this->start, $this->end, null, new Collection([$budget]), null);
|
||||
/** @var array $entry */
|
||||
foreach ($spent as $entry) {
|
||||
$title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
|
||||
$data[0]['entries'][$title] = bcmul($entry['sum'], '-1'); // spent
|
||||
$data[1]['entries'][$title] = 0; // left to spend
|
||||
$data[2]['entries'][$title] = 0; // overspent
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a budget has budget limit, each limit is processed individually.
|
||||
*
|
||||
* @param array $data
|
||||
* @param Budget $budget
|
||||
* @param Collection $limits
|
||||
* @return array
|
||||
*/
|
||||
private function budgetLimits(array $data, Budget $budget, Collection $limits): array
|
||||
{
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($limits as $limit) {
|
||||
$data = $this->processLimit($data, $budget, $limit);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* For each limit, the expenses from the time range of the limit are collected. Each row from the result is processed individually.
|
||||
*
|
||||
* @param array $data
|
||||
* @param Budget $budget
|
||||
* @param BudgetLimit $limit
|
||||
* @return array
|
||||
*/
|
||||
private function processLimit(array $data, Budget $budget, BudgetLimit $limit): array
|
||||
{
|
||||
$spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $limit->transactionCurrency);
|
||||
/** @var array $entry */
|
||||
foreach ($spent as $entry) {
|
||||
$data = $this->processRow($data, $budget, $limit, $entry);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each row of expenses from a budget limit is in another currency (note $entry['currency_name']).
|
||||
*
|
||||
* Each one is added to the $data array. If the limit's date range is different from the global $start and $end dates,
|
||||
* for example when a limit only partially falls into this month, the title is expanded to clarify.
|
||||
*
|
||||
* @param array $data
|
||||
* @param Budget $budget
|
||||
* @param BudgetLimit $limit
|
||||
* @param array $entry
|
||||
* @return array
|
||||
*/
|
||||
private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array
|
||||
{
|
||||
$title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
|
||||
if ($limit->start_date->startOfDay()->ne($this->start->startOfDay()) || $limit->end_date->startOfDay()->ne($this->end->startOfDay())) {
|
||||
$title = sprintf(
|
||||
'%s (%s) (%s - %s)',
|
||||
$budget->name,
|
||||
$entry['currency_name'],
|
||||
$limit->start_date->formatLocalized($this->monthAndDayFormat),
|
||||
$limit->end_date->formatLocalized($this->monthAndDayFormat)
|
||||
);
|
||||
}
|
||||
$sumSpent = bcmul($entry['sum'], '-1'); // spent
|
||||
|
||||
$data[0]['entries'][$title] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent; // spent
|
||||
$data[1]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($entry['sum'], $limit->amount) : '0'; // left to spent
|
||||
$data[2]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ? '0' : bcmul(bcadd($entry['sum'], $limit->amount), '-1'); // overspent
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user