Some fixing up for #1598

This commit is contained in:
James Cole 2018-08-07 17:34:43 +02:00
parent 5908c0ce8c
commit b496ca6a2c
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
4 changed files with 134 additions and 120 deletions

View File

@ -80,24 +80,34 @@ class AmountController extends Controller
*/
public function amount(Request $request, BudgetRepositoryInterface $repository, Budget $budget): JsonResponse
{
// grab vars from URI
$amount = (string)$request->get('amount');
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
// grab other useful vars
$currency = app('amount')->getDefaultCurrency();
$activeDaysLeft = $this->activeDaysLeft($start, $end);
$periodLength = $start->diffInDays($end) + 1; // absolute period length.
// update limit amount:
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
// calculate what the user has spent in current period.
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
// given the new budget, this is what they have left (and left per day?)
$left = app('amount')->formatAnything($currency, bcadd($amount, $spent), true);
$largeDiff = false;
$warnText = '';
$leftPerDay = null;
$periodLength = $start->diffInDays($end);
$dayDifference = $this->getDayDifference($start, $end);
$leftPerDay = null; //
// If the user budgets ANY amount per day for this budget (anything but zero) Firefly III calculates how much he could spend per day.
if (1 === bccomp(bcadd($amount, $spent), '0')) {
$leftPerDay = app('amount')->formatAnything($currency, bcdiv(bcadd($amount, $spent), (string)$dayDifference), true);
$leftPerDay = app('amount')->formatAnything($currency, bcdiv(bcadd($amount, $spent), (string)$activeDaysLeft), true);
}
$largeDiff = false;
$warnText = '';
// Get the average amount of money the user budgets for this budget. And calculate the same for the current amount.
// If the difference is very large, give the user a notification.
$average = $this->repository->budgetedPerDay($budget);
@ -116,8 +126,17 @@ class AmountController extends Controller
app('preferences')->mark();
return response()->json(
['left' => $left, 'name' => $budget->name, 'limit' => $budgetLimit ? $budgetLimit->id : 0, 'amount' => $amount, 'current' => $current,
'average' => $average, 'large_diff' => $largeDiff, 'left_per_day' => $leftPerDay, 'warn_text' => $warnText,]
[
'left' => $left,
'name' => $budget->name,
'limit' => $budgetLimit ? $budgetLimit->id : 0,
'amount' => $amount,
'current' => $current,
'average' => $average,
'large_diff' => $largeDiff,
'left_per_day' => $leftPerDay,
'warn_text' => $warnText,
]
);
}

View File

@ -58,6 +58,7 @@ class IndexController extends Controller
app('view')->share('title', (string)trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
$this->repository->cleanupBudgets();
return $next($request);
}
@ -77,17 +78,16 @@ class IndexController extends Controller
*/
public function index(Request $request, string $moment = null)
{
/** @var string $range */
// collect some basic vars:
$range = app('preferences')->get('viewRange', '1M')->data;
/** @var Carbon $start */
$start = session('start', new Carbon);
/** @var Carbon $end */
$end = session('end', new Carbon);
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$moment = $moment ?? '';
$defaultCurrency = app('amount')->getDefaultCurrency();
// make date if the data is given.
// make a date if the data is given.
if ('' !== (string)$moment) {
try {
$start = new Carbon($moment);
@ -99,44 +99,49 @@ class IndexController extends Controller
}
}
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$dayDifference = $this->getDayDifference($start, $end);
// make the next and previous period, and calculate the periods used for period navigation
$next = clone $end;
$next->addDay();
$prev = clone $start;
$prev->subDay();
$prev = app('navigation')->startOfPeriod($prev, $range);
$this->repository->cleanupBudgets();
$daysPassed = $this->getDaysPassedInPeriod($start, $end);
$allBudgets = $this->repository->getActiveBudgets();
$total = $allBudgets->count();
$budgets = $allBudgets->slice(($page - 1) * $pageSize, $pageSize);
$inactive = $this->repository->getInactiveBudgets();
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
$budgetInformation = $this->repository->collectBudgetInformation($allBudgets, $start, $end);
$defaultCurrency = app('amount')->getDefaultCurrency();
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
$spent = array_sum(array_column($budgetInformation, 'spent'));
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
$previousLoop = $this->getPreviousPeriods($start, $range);
$nextLoop = $this->getNextPeriods($end, $range);
// paginate budgets
$budgets = new LengthAwarePaginator($budgets, $total, $pageSize, $page);
$budgets->setPath(route('budgets.index'));
// display info
$currentMonth = app('navigation')->periodShow($start, $range);
$nextText = app('navigation')->periodShow($next, $range);
$prevText = app('navigation')->periodShow($prev, $range);
// number of days for consistent budgeting.
$activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description.
$activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description.
// get all budgets, and paginate them into $budgets.
$collection = $this->repository->getActiveBudgets();
$total = $collection->count();
$budgets = $collection->slice(($page - 1) * $pageSize, $pageSize);
// get all inactive budgets, and simply list them:
$inactive = $this->repository->getInactiveBudgets();
// collect budget info to fill bars and so on.
$budgetInformation = $this->repository->collectBudgetInformation($collection, $start, $end);
// to display available budget:
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
$spent = array_sum(array_column($budgetInformation, 'spent'));
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
// paginate budgets
$paginator = new LengthAwarePaginator($budgets, $total, $pageSize, $page);
$paginator->setPath(route('budgets.index'));
return view(
'budgets.index', compact(
'available', 'currentMonth', 'next', 'nextText', 'prev', 'allBudgets', 'prevText', 'periodStart', 'periodEnd', 'dayDifference',
'page',
'budgetInformation', 'daysPassed',
'available', 'currentMonth', 'next', 'nextText', 'prev', 'paginator',
'prevText',
'page', 'activeDaysPassed', 'activeDaysLeft',
'budgetInformation',
'inactive', 'budgets', 'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start', 'end'
)
);

View File

@ -32,6 +32,52 @@ use Log;
*/
trait DateCalculation
{
/**
* Calculate the number of days passed left until end date, as seen from start date.
* If today is between start and end, today will be used instead of end.
*
* If both are in the past OR both are in the future, simply return the number of days in the period with a minimum of 1
*
* @param Carbon $start
* @param Carbon $end
*
* @return int
*/
public function activeDaysLeft(Carbon $start, Carbon $end): int
{
$difference = $start->diffInDays($end) + 1;
$today = Carbon::now()->startOfDay();
if ($start->lte($today) && $end->gte($today)) {
$difference = $today->diffInDays($end);
}
$difference = 0 === $difference ? 1 : $difference;
return $difference;
}
/**
* Calculate the number of days passed between two dates. Will take the current moment into consideration.
*
* If both are in the past OR both are in the future, simply return the period between them with a minimum of 1
*
* @param Carbon $start
* @param Carbon $end
*
* @return int
*/
protected function activeDaysPassed(Carbon $start, Carbon $end): int
{
$difference = $start->diffInDays($end) + 1;
$today = Carbon::now()->startOfDay();
if ($start->lte($today) && $end->gte($today)) {
$difference = $start->diffInDays($today) + 1;
}
return $difference;
}
/**
* @param Carbon $start
* @param Carbon $end
@ -56,62 +102,6 @@ trait DateCalculation
return $step;
}
/**
* Returns the number of days between the two given dates.
* - If today is within the two dates, give the number of days between today and the end date.
* - If they are the same, return 1.
*
* @param Carbon $start
* @param Carbon $end
*
* @return int
*/
protected function getDayDifference(Carbon $start, Carbon $end): int
{
$dayDifference = 0;
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
Log::debug(sprintf('Start is %s, end is %s, today is %s', $start->format('Y-m-d'), $end->format('Y-m-d'), $today->format('Y-m-d')));
if ($today->gte($start) && $today->lte($end)) {
$dayDifference = $end->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$dayDifference = $start->diffInDays($end);
}
$dayDifference = 0 === $dayDifference ? 1 : $dayDifference;
return $dayDifference;
}
/**
* Returns the number of days that have passed in this period. If it is zero (start of period)
* then return 1.
*
* @param Carbon $start
* @param Carbon $end
*
* @return int
*/
protected function getDaysPassedInPeriod(Carbon $start, Carbon $end): int
{
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
$daysPassed = 0;
Log::debug(sprintf('Start is %s, end is %s, today is %s', $start->format('Y-m-d'), $end->format('Y-m-d'), $today->format('Y-m-d')));
if ($today->gte($start) && $today->lte($end)) {
$daysPassed = $start->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$daysPassed = $start->diffInDays($end);
}
$daysPassed = 0 === $daysPassed ? 1 : $daysPassed;
return $daysPassed;
}
/**
* Get a list of the periods that will occur after this date. For example,

View File

@ -9,7 +9,7 @@
<div class="col-lg-9 col-md-8 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ periodStart }} &mdash; {{ periodEnd }}</h3>
<h3 class="box-title">{{ start.formatLocalized(monthAndDayFormat) }} &mdash; {{ end.formatLocalized(monthAndDayFormat) }}</h3>
</div>
<div class="box-body">
<div class="row">
@ -17,7 +17,7 @@
<small>{{ 'budgeted'|_ }}: <span id="budgetedAmount" class="text-success">{{ budgeted|formatAmountPlain }}</span></small>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9" style="text-align:right;margin-bottom:3px;">
<small id="availableBar">{{ trans('firefly.available_between',{start : periodStart, end: periodEnd }) }}:
<small id="availableBar">{{ trans('firefly.available_between',{start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat) }) }}:
<span id="available" data-value="{{ available }}">{{ available|formatAmountPlain }}</span>
<a href="#" class="updateIncome btn btn-default btn-xs"><i class="fa fa-pencil"></i></a>
<a href="#" class="infoIncome btn btn-info btn-xs"><i class="fa fa-info-circle"></i></a>
@ -39,7 +39,7 @@
</div>
<div class="row" id="spentBar">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<small>{{ trans('firefly.spent_between', {start: periodStart, end: periodEnd}) }}: {{ spent|formatAmount }}</small>
<small>{{ trans('firefly.spent_between', {start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}: {{ spent|formatAmount }}</small>
</div>
</div>
<div class="row">
@ -66,12 +66,12 @@
<div class="box-body">
<p>
<a href="{{ route('budgets.no-budget', [start.format('Y-m-d'), end.format('Y-m-d')]) }}">
{{ trans('firefly.transactions_no_budget', {start: periodStart, end: periodEnd }) }}
{{ trans('firefly.transactions_no_budget', {start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}
</a>
</p>
</div>
</div>
{% if budgets.count > 0 and inactive.count > 0 %}
{% if paginator.count > 0 and inactive.count > 0 %}
<div class="box" id="createBudgetBox">
<div class="box-header with-border">
<h3 class="box-title">{{ 'createBudget'|_ }}</h3>
@ -83,7 +83,7 @@
{% endif %}
</div>
</div>
{% if budgets.count == 0 and inactive.count == 0 and page == 1 %}
{% if paginator.count == 0 and inactive.count == 0 and page == 1 %}
{% include 'partials.empty' with {what: 'default', type: 'budgets',route: route('budgets.create')} %}
{# make FF ignore demo for now. #}
{% set shownDemo = true %}
@ -138,7 +138,7 @@
<a href="{{ route('budgets.create') }}" class="btn btn-success"><i class="fa fa-plus fa-fw"></i> {{ 'createBudget'|_ }}</a>
</div>
<div style="padding-left:8px;">
{{ budgets.render|raw }}
{{ paginator.render|raw }}
</div>
<table class="table table-bordered table-striped sortable" id="budgetList">
<thead>
@ -152,7 +152,7 @@
</thead>
<tbody>
<tr>
{% for budget in budgets %}
{% for budget in paginator %}
<tr>
<td class="hidden-sm hidden-xs">
<div class="btn-group btn-group-xs">
@ -188,13 +188,13 @@
<td class="hidden-sm hidden-xs spent" data-id="{{ budget.id }}" data-spent="{{ budgetInformation[budget.id]['spent'] }}"
data-value="{{ budgetInformation[budget.id]['spent'] }}">
{{ budgetInformation[budget.id]['spent']|formatAmount }}
({{ (budgetInformation[budget.id]['spent'] / daysPassed)|formatAmount }})
({{ (budgetInformation[budget.id]['spent'] / activeDaysPassed)|formatAmount }})
</td>
<td class="left" data-id="{{ budget.id }}"
data-value="{{ (repAmount + budgetInformation[budget.id]['spent']) }}">
{{ (repAmount + budgetInformation[budget.id]['spent'])|formatAmount }}
{% if repAmount + budgetInformation[budget.id]['spent'] > 0 %}
({{ ((repAmount + budgetInformation[budget.id]['spent']) / daysPassed)|formatAmount }})
({{ ((repAmount + budgetInformation[budget.id]['spent']) / activeDaysLeft)|formatAmount }})
{% endif %}
</td>
</tr>
@ -202,7 +202,7 @@
</tbody>
</table>
<div style="padding-left:8px;">
{{ budgets.render|raw }}
{{ paginator.render|raw }}
</div>
</div>
<div class="box-footer">