diff --git a/app/Http/Controllers/Budget/AmountController.php b/app/Http/Controllers/Budget/AmountController.php index 3753265b1d..0b9e223dd2 100644 --- a/app/Http/Controllers/Budget/AmountController.php +++ b/app/Http/Controllers/Budget/AmountController.php @@ -80,24 +80,34 @@ class AmountController extends Controller */ public function amount(Request $request, BudgetRepositoryInterface $repository, Budget $budget): JsonResponse { - $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); - $currency = app('amount')->getDefaultCurrency(); - $left = app('amount')->formatAnything($currency, bcadd($amount, $spent), true); - $largeDiff = false; - $warnText = ''; - $leftPerDay = null; - $periodLength = $start->diffInDays($end); - $dayDifference = $this->getDayDifference($start, $end); + // 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')); + + // 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); + $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, + ] ); } diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 726a730356..fce1688a6c 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -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 */ - $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 ?? ''; + // collect some basic vars: + $range = app('preferences')->get('viewRange', '1M')->data; + $start = session('start', new Carbon); + $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 + $prev = app('navigation')->startOfPeriod($prev, $range); + $previousLoop = $this->getPreviousPeriods($start, $range); + $nextLoop = $this->getNextPeriods($end, $range); $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' ) ); diff --git a/app/Support/Http/Controllers/DateCalculation.php b/app/Support/Http/Controllers/DateCalculation.php index 03c9df546a..7c08f640ec 100644 --- a/app/Support/Http/Controllers/DateCalculation.php +++ b/app/Support/Http/Controllers/DateCalculation.php @@ -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, diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig index 97cba2e2e3..73e175b45a 100644 --- a/resources/views/budgets/index.twig +++ b/resources/views/budgets/index.twig @@ -9,7 +9,7 @@
-

{{ periodStart }} — {{ periodEnd }}

+

{{ start.formatLocalized(monthAndDayFormat) }} — {{ end.formatLocalized(monthAndDayFormat) }}

@@ -17,7 +17,7 @@ {{ 'budgeted'|_ }}: {{ budgeted|formatAmountPlain }}
- {{ trans('firefly.available_between',{start : periodStart, end: periodEnd }) }}: + {{ trans('firefly.available_between',{start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat) }) }}: {{ available|formatAmountPlain }} @@ -39,7 +39,7 @@
- {{ trans('firefly.spent_between', {start: periodStart, end: periodEnd}) }}: {{ spent|formatAmount }} + {{ trans('firefly.spent_between', {start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}: {{ spent|formatAmount }}
- {% if budgets.count > 0 and inactive.count > 0 %} + {% if paginator.count > 0 and inactive.count > 0 %}

{{ 'createBudget'|_ }}

@@ -83,7 +83,7 @@ {% endif %}
- {% 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 @@ {{ 'createBudget'|_ }}
- {{ budgets.render|raw }} + {{ paginator.render|raw }}
@@ -152,7 +152,7 @@ - {% for budget in budgets %} + {% for budget in paginator %} @@ -202,7 +202,7 @@
{{ (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 %}
- {{ budgets.render|raw }} + {{ paginator.render|raw }}