This commit is contained in:
James Cole 2020-07-25 08:03:57 +02:00
parent 02ad8273eb
commit 5e1306282a
No known key found for this signature in database
GPG Key ID: B5669F9493CDE38D
2 changed files with 214 additions and 138 deletions

View File

@ -98,138 +98,56 @@ class IndexController extends Controller
public function index(Request $request, Carbon $start = null, Carbon $end = null) public function index(Request $request, Carbon $start = null, Carbon $end = null)
{ {
Log::debug('Start of IndexController::index()'); Log::debug('Start of IndexController::index()');
// collect some basic vars: // collect some basic vars:
$range = app('preferences')->get('viewRange', '1M')->data; $range = app('preferences')->get('viewRange', '1M')->data;
$start = $start ?? session('start', Carbon::now()->startOfMonth()); $start = $start ?? session('start', Carbon::now()->startOfMonth());
$end = $end ?? app('navigation')->endOfPeriod($start, $range); $end = $end ?? app('navigation')->endOfPeriod($start, $range);
$defaultCurrency = app('amount')->getDefaultCurrency(); $defaultCurrency = app('amount')->getDefaultCurrency();
$currencies = $this->currencyRepository->getEnabled();
$budgeted = '0'; $budgeted = '0';
$spent = '0'; $spent = '0';
Log::debug(sprintf('1) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// new period stuff: // new period stuff:
$periodTitle = app('navigation')->periodShow($start, $range); $periodTitle = app('navigation')->periodShow($start, $range);
$prevLoop = $this->getPreviousPeriods($start, $range); $prevLoop = $this->getPreviousPeriods($start, $range);
$nextLoop = $this->getNextPeriods($start, $range); $nextLoop = $this->getNextPeriods($start, $range);
Log::debug(sprintf('2) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// get all available budgets. // get all available budgets:
$ab = $this->abRepository->get($start, $end); $availableBudgets = $this->getAllAvailableBudgets($start, $end);
$availableBudgets = [];
Log::debug(sprintf('3) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// for each, complement with spent amount:
/** @var AvailableBudget $entry */
foreach ($ab as $entry) {
$array = $entry->toArray();
$array['start_date'] = $entry->start_date;
$array['end_date'] = $entry->end_date;
// spent in period: // get all active budgets:
$spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency); $budgets = $this->getAllBudgets($start, $end, $currencies, $defaultCurrency);
$array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0'; $sums = $this->getSums($budgets);
// budgeted in period:
$budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency, );
$array['budgeted'] = $budgeted;
$availableBudgets[] = $array;
unset($spentArr);
Log::debug(sprintf('4) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
}
if (0 === count($availableBudgets)) {
// get budgeted for default currency: // get budgeted for default currency:
$budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency, ); if (0 === count($availableBudgets)) {
$budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency,);
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, null, $defaultCurrency); $spentArr = $this->opsRepository->sumExpenses($start, $end, null, null, $defaultCurrency);
$spent = $spentArr[$defaultCurrency->id]['sum'] ?? '0'; $spent = $spentArr[$defaultCurrency->id]['sum'] ?? '0';
unset($spentArr); unset($spentArr);
Log::debug(sprintf('5) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
} }
// count the number of enabled currencies. This determines if we display a "+" button. // count the number of enabled currencies. This determines if we display a "+" button.
$currencies = $this->currencyRepository->getEnabled();
$enableAddButton = $currencies->count() > count($availableBudgets); $enableAddButton = $currencies->count() > count($availableBudgets);
// number of days for consistent budgeting. // number of days for consistent budgeting.
$activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description. $activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description.
$activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description. $activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description.
Log::debug(sprintf('6) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// get all budgets, and paginate them into $budgets.
$collection = $this->repository->getActiveBudgets();
$budgets = [];
Log::debug(sprintf('7) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// complement budget with budget limits in range, and expenses in currency X in range.
/** @var Budget $current */
foreach ($collection as $current) {
$array = $current->toArray();
$array['spent'] = [];
$array['budgeted'] = [];
$array['attachments'] = $this->repository->getAttachments($current);
$array['auto_budget'] = $this->repository->getAutoBudget($current);
$budgetLimits = $this->blRepository->getBudgetLimits($current, $start, $end);
Log::debug(sprintf('8) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
$currency = $limit->transactionCurrency ?? $defaultCurrency;
$array['budgeted'][] = [
'id' => $limit->id,
'amount' => number_format((float) $limit->amount, $currency->decimal_places, '.', ''),
'start_date' => $limit->start_date->formatLocalized($this->monthAndDayFormat),
'end_date' => $limit->end_date->formatLocalized($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end),
'currency_id' => $currency->id,
'currency_symbol' => $currency->symbol,
'currency_name' => $currency->name,
'currency_decimal_places' => $currency->decimal_places,
];
Log::debug(sprintf('9) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
}
/** @var TransactionCurrency $currency */
foreach ($currencies as $currency) {
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency);
if (isset($spentArr[$currency->id]['sum'])) {
$array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum'];
$array['spent'][$currency->id]['currency_id'] = $currency->id;
$array['spent'][$currency->id]['currency_symbol'] = $currency->symbol;
$array['spent'][$currency->id]['currency_decimal_places'] = $currency->decimal_places;
Log::debug(sprintf('10) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
}
}
$budgets[] = $array;
}
// get all inactive budgets, and simply list them: // get all inactive budgets, and simply list them:
$inactive = $this->repository->getInactiveBudgets(); $inactive = $this->repository->getInactiveBudgets();
Log::debug(sprintf('11) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
return view( return view(
'budgets.index', 'budgets.index', compact(
compact( 'availableBudgets', 'budgeted', 'spent', 'prevLoop', 'nextLoop', 'budgets', 'currencies', 'enableAddButton', 'periodTitle',
'availableBudgets', 'defaultCurrency', 'activeDaysPassed', 'activeDaysLeft', 'inactive', 'budgets', 'start', 'end', 'sums'
'budgeted',
'spent',
'prevLoop',
'nextLoop',
'budgets',
'currencies',
'enableAddButton',
'periodTitle',
'defaultCurrency',
'activeDaysPassed',
'activeDaysLeft',
'inactive',
'budgets',
'start',
'end'
) )
); );
} }
/** /**
* @param Request $request * @param Request $request
*
* @param BudgetRepositoryInterface $repository * @param BudgetRepositoryInterface $repository
* *
* @return JsonResponse * @return JsonResponse
@ -250,4 +168,154 @@ class IndexController extends Controller
return response()->json(['OK']); return response()->json(['OK']);
} }
/**
* @param array $budgets
*
* @return array
*/
private function getSums(array $budgets): array
{
$sums = [
'budgeted' => [],
'spent' => [],
'left' => [],
];
/** @var array $budget */
foreach ($budgets as $budget) {
/** @var array $spent */
foreach ($budget['spent'] as $spent) {
$currencyId = $spent['currency_id'];
$sums['spent'][$currencyId]
= $sums['spent'][$currencyId]
?? [
'amount' => '0',
'currency_id' => $spent['currency_id'],
'currency_symbol' => $spent['currency_symbol'],
'currency_decimal_places' => $spent['currency_decimal_places'],
];
$sums['spent'][$currencyId]['amount'] = bcadd($sums['spent'][$currencyId]['amount'], $spent['spent']);
}
/** @var array $budgeted */
foreach ($budget['budgeted'] as $budgeted) {
$currencyId = $budgeted['currency_id'];
$sums['budgeted'][$currencyId]
= $sums['budgeted'][$currencyId]
?? [
'amount' => '0',
'currency_id' => $budgeted['currency_id'],
'currency_symbol' => $budgeted['currency_symbol'],
'currency_decimal_places' => $budgeted['currency_decimal_places'],
];
$sums['budgeted'][$currencyId]['amount'] = bcadd($sums['budgeted'][$currencyId]['amount'], $budgeted['amount']);
// also calculate how much left from budgeted:
$sums['left'][$currencyId] = $sums['left'][$currencyId]
?? [
'amount' => '0',
'currency_id' => $budgeted['currency_id'],
'currency_symbol' => $budgeted['currency_symbol'],
'currency_decimal_places' => $budgeted['currency_decimal_places'],
];
}
}
// final calculation for 'left':
foreach ($sums['budgeted'] as $currencyId => $info) {
$spent = $sums['spent'][$currencyId]['amount'] ?? '0';
$budgeted = $sums['budgeted'][$currencyId]['amount'] ?? '0';
$sums['left'][$currencyId]['amount'] = bcadd($spent, $budgeted);
}
return $sums;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
private function getAllAvailableBudgets(Carbon $start, Carbon $end): array
{
// get all available budgets.
$ab = $this->abRepository->get($start, $end);
$availableBudgets = [];
// for each, complement with spent amount:
/** @var AvailableBudget $entry */
foreach ($ab as $entry) {
$array = $entry->toArray();
$array['start_date'] = $entry->start_date;
$array['end_date'] = $entry->end_date;
// spent in period:
$spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency);
$array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0';
// budgeted in period:
$budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency,);
$array['budgeted'] = $budgeted;
$availableBudgets[] = $array;
unset($spentArr);
}
return $availableBudgets;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $currencies
* @param TransactionCurrency $defaultCurrency
*
* @return array
*/
private function getAllBudgets(Carbon $start, Carbon $end, Collection $currencies, TransactionCurrency $defaultCurrency): array
{
// get all budgets, and paginate them into $budgets.
$collection = $this->repository->getActiveBudgets();
$budgets = [];
Log::debug(sprintf('7) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// complement budget with budget limits in range, and expenses in currency X in range.
/** @var Budget $current */
foreach ($collection as $current) {
$array = $current->toArray();
$array['spent'] = [];
$array['budgeted'] = [];
$array['attachments'] = $this->repository->getAttachments($current);
$array['auto_budget'] = $this->repository->getAutoBudget($current);
$budgetLimits = $this->blRepository->getBudgetLimits($current, $start, $end);
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
$currency = $limit->transactionCurrency ?? $defaultCurrency;
$array['budgeted'][] = [
'id' => $limit->id,
'amount' => number_format((float) $limit->amount, $currency->decimal_places, '.', ''),
'start_date' => $limit->start_date->formatLocalized($this->monthAndDayFormat),
'end_date' => $limit->end_date->formatLocalized($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end),
'currency_id' => $currency->id,
'currency_symbol' => $currency->symbol,
'currency_name' => $currency->name,
'currency_decimal_places' => $currency->decimal_places,
];
}
/** @var TransactionCurrency $currency */
foreach ($currencies as $currency) {
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency);
if (isset($spentArr[$currency->id]['sum'])) {
$array['spent'][$currency->id]['spent'] = $spentArr[$currency->id]['sum'];
$array['spent'][$currency->id]['currency_id'] = $currency->id;
$array['spent'][$currency->id]['currency_symbol'] = $currency->symbol;
$array['spent'][$currency->id]['currency_decimal_places'] = $currency->decimal_places;
}
}
$budgets[] = $array;
}
return $budgets;
}
} }

View File

@ -216,11 +216,11 @@
<table class="table table-bordered sortable-table table-striped sortable" id="budgetList"> <table class="table table-bordered sortable-table table-striped sortable" id="budgetList">
<thead> <thead>
<tr> <tr>
<th data-defaultsort="disabled" class="hidden-sm hidden-xs" style="width:10%;">&nbsp;</th> <th class="hidden-sm hidden-xs" style="width:10%;">&nbsp;</th>
<th data-defaultsign="az">{{ 'budget'|_ }}</th> <th>{{ 'budget'|_ }}</th>
<th data-defaultsign="_19" style="width:25%;">{{ 'budgeted'|_ }}</th> <th style="width:25%;">{{ 'budgeted'|_ }}</th>
<th data-defaultsign="_19" class="hidden-sm hidden-xs">{{ 'spent'|_ }} ({{ 'per_day'|_|lower }})</th> <th class="hidden-sm hidden-xs">{{ 'spent'|_ }} ({{ 'per_day'|_|lower }})</th>
<th data-defaultsign="_19">{{ 'left'|_ }} ({{ 'per_day'|_|lower }})</th> <th>{{ 'left'|_ }} ({{ 'per_day'|_|lower }})</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -228,12 +228,12 @@
<tr data-id="{{ budget.id }}"> <tr data-id="{{ budget.id }}">
<td class="hidden-sm hidden-xs"> <td class="hidden-sm hidden-xs">
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a href="#" class="handle btn btn-default"><i class="fa fa-fw fa-arrows-v"></i></a> <a href="#" class="handle object-handle btn btn-default"><i class="fa fa-fw fa-bars"></i></a>
<a href="{{ route('budgets.edit', budget.id) }}" class="btn btn-xs btn-default"><i class="fa fa-fw fa-pencil"></i></a> <a href="{{ route('budgets.edit', budget.id) }}" class="btn btn-xs btn-default"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{{ route('budgets.delete', budget.id) }}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a> <a href="{{ route('budgets.delete', budget.id) }}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a>
</div> </div>
</td> </td>
<td data-value="{{ budget.name }}"> <td>
<a href="{{ route('budgets.show',budget.id) }}" data-id="{{ budget.id }}">{{ budget.name }}</a> <a href="{{ route('budgets.show',budget.id) }}" data-id="{{ budget.id }}">{{ budget.name }}</a>
{% if budget.auto_budget %} {% if budget.auto_budget %}
{% if 1 == budget.auto_budget.auto_budget_type %} {% if 1 == budget.auto_budget.auto_budget_type %}
@ -247,7 +247,7 @@
<i class="fa fa-paperclip"></i> <i class="fa fa-paperclip"></i>
{% endif %} {% endif %}
</td> </td>
<td data-value="-1"> <td>
{% if 0==budget.budgeted|length %} {% if 0==budget.budgeted|length %}
<div class="input-group"> <div class="input-group">
<div class="input-group-addon">{{ defaultCurrency.symbol }}</div> <div class="input-group-addon">{{ defaultCurrency.symbol }}</div>
@ -290,15 +290,19 @@
</a> </a>
{% endif %} {% endif %}
</td> </td>
<td class="hidden-sm hidden-xs spent" data-id="{{ budget.id }}"> <td class="hidden-sm hidden-xs spent" data-id="{{ budget.id }}" style="text-align:right;">
{% for spentInfo in budget.spent %} {% for spentInfo in budget.spent %}
{{ formatAmountBySymbol(spentInfo.spent, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }} {{ formatAmountBySymbol(spentInfo.spent, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}
({{ formatAmountBySymbol(spentInfo.spent / activeDaysPassed, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}) ({{ formatAmountBySymbol(spentInfo.spent / activeDaysPassed, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
<br/> <br/>
{% endfor %} {% endfor %}
{% for budgetLimit in budget.budgeted %}
{% if null == budget.spent[budgetLimit.currency_id] %}
{{ formatAmountBySymbol(0, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }}<br/>
{% endif %}
{% endfor %}
</td> </td>
<td class="left" data-id="{{ budget.id }}"> <td class="left" data-id="{{ budget.id }}" style="text-align: right;">
{% for spentInfo in budget.spent %} {% for spentInfo in budget.spent %}
{% set countLimit = 0 %} {% set countLimit = 0 %}
{% for budgetLimit in budget.budgeted %} {% for budgetLimit in budget.budgeted %}
@ -324,41 +328,45 @@
<br/> <br/>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<!-- only makes sense to list what's left for budgeted amounts.-->
<!--
{% if budget.budgeted|length > 0 %}
{% for budgetLimit in budget.budgeted %} {% for budgetLimit in budget.budgeted %}
{% for spentInfo in budget.spent %} {% if null == budget.spent[budgetLimit.currency_id] %}
{% if spentInfo.currency_id == budgetLimit.currency_id %} <span class="left_span" data-currency="{{ spentInfo.currency_id }}" data-limit="{{ budgetLimit.id }}"
<span data-currency="{{ spentInfo.currency_id }}" data-limit="{{ budgetLimit.id }}" class="amount_left"> data-value="{{ spentInfo.spent + budgetLimit.amount }}" class="amount_left">
{{ formatAmountBySymbol(spentInfo.spent + budgetLimit.amount, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }} {{ formatAmountBySymbol(budgetLimit.amount, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }}
{% if spentInfo.spent + budgetLimit.amount > 0 %} ({{ formatAmountBySymbol(budgetLimit.amount / activeDaysLeft, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
({{ formatAmountBySymbol((spentInfo.spent + budgetLimit.amount) / activeDaysLeft, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
{% else %}
({{ formatAmountBySymbol(0, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
{% endif %}
</span> </span>
<br/> <br/>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endfor %}
{% endif %}
-->
{#{{ "-1"|formatAmount }}#}
{#{{ (repAmount + budgetInformation[budget.id]['spent'])|formatAmount }}#}
{#{% if repAmount + budgetInformation[budget.id]['spent'] > 0 %}#}
{#({{ "-1"|formatAmount }})#}
{#({{ ((repAmount + budgetInformation[budget.id]['spent']) / activeDaysLeft)|formatAmount }})#}
{#{% endif %}#}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
<tfoot>
<tr>
<td>&nbsp;</td>
<td><em>{{ 'sum'|_ }}</em></td>
<td style="text-align:right;">
{% for arr in sums.budgeted %}
{{ formatAmountBySymbol(arr.amount, arr.currency_symbol, arr.currency_decimal_places) }}<br/>
{% endfor %}
</td>
<td style="text-align:right;">
{% for arr in sums.spent %}
{{ formatAmountBySymbol(arr.amount, arr.currency_symbol, arr.currency_decimal_places) }}
({{ formatAmountBySymbol(arr.amount / activeDaysPassed, arr.currency_symbol, arr.currency_decimal_places) }})
<br/>
{% endfor %}
</td>
<td style="text-align: right;">
{% for arr in sums.left %}
{{ formatAmountBySymbol(arr.amount, arr.currency_symbol, arr.currency_decimal_places) }}
({{ formatAmountBySymbol(arr.amount / activeDaysLeft, arr.currency_symbol, arr.currency_decimal_places) }})
<br/>
{% endfor %}
</td>
</tr>
</tfoot>
</table> </table>
</div> </div>
<div class="box-footer"> <div class="box-footer">