mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-12 23:45:10 +00:00
Code improvements for budgets.
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
use Amount;
|
use Amount;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Config;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Http\Requests\BudgetFormRequest;
|
use FireflyIII\Http\Requests\BudgetFormRequest;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
@@ -142,18 +143,22 @@ class BudgetController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(BudgetRepositoryInterface $repository, ARI $accountRepository)
|
public function index(BudgetRepositoryInterface $repository, ARI $accountRepository)
|
||||||
{
|
{
|
||||||
$budgets = $repository->getActiveBudgets();
|
$budgets = $repository->getActiveBudgets();
|
||||||
$inactive = $repository->getInactiveBudgets();
|
$inactive = $repository->getInactiveBudgets();
|
||||||
$spent = '0';
|
$spent = '0';
|
||||||
$budgeted = '0';
|
$budgeted = '0';
|
||||||
$range = Preferences::get('viewRange', '1M')->data;
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
|
$repeatFreq = Config::get('firefly.range_to_repeat_freq.' . $range);
|
||||||
/** @var Carbon $date */
|
/** @var Carbon $date */
|
||||||
$date = session('start', new Carbon);
|
/** @var Carbon $start */
|
||||||
$start = Navigation::startOfPeriod($date, $range);
|
$start = session('start', new Carbon);
|
||||||
$end = Navigation::endOfPeriod($start, $range);
|
/** @var Carbon $end */
|
||||||
|
$end = session('end', new Carbon);
|
||||||
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
$key = 'budgetIncomeTotal' . $start->format('Ymd') . $end->format('Ymd');
|
||||||
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
|
$budgetIncomeTotal = Preferences::get($key, 1000)->data;
|
||||||
$period = Navigation::periodShow($start, $range);
|
$period = Navigation::periodShow($start, $range);
|
||||||
|
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||||
|
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||||
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
|
$accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,8 +169,9 @@ class BudgetController extends Controller
|
|||||||
// loop the budgets:
|
// loop the budgets:
|
||||||
/** @var Budget $budget */
|
/** @var Budget $budget */
|
||||||
foreach ($budgets as $budget) {
|
foreach ($budgets as $budget) {
|
||||||
$budget->spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
|
$budget->spent = $repository->balanceInPeriod($budget, $start, $end, $accounts);
|
||||||
$budget->currentRep = $repository->getCurrentRepetition($budget, $start, $end);
|
$budget->currentRep = $repository->getCurrentRepetition($budget, $repeatFreq, $start, $end);
|
||||||
|
$budget->otherRepetitions = $repository->getValidRepetitions($budget, $start, $end, $budget->currentRep);
|
||||||
if (!is_null($budget->currentRep->id)) {
|
if (!is_null($budget->currentRep->id)) {
|
||||||
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
|
$budgeted = bcadd($budgeted, $budget->currentRep->amount);
|
||||||
}
|
}
|
||||||
@@ -178,7 +184,12 @@ class BudgetController extends Controller
|
|||||||
$defaultCurrency = Amount::getDefaultCurrency();
|
$defaultCurrency = Amount::getDefaultCurrency();
|
||||||
|
|
||||||
return view(
|
return view(
|
||||||
'budgets.index', compact('budgetMaximum', 'period', 'range', 'budgetIncomeTotal', 'defaultCurrency', 'inactive', 'budgets', 'spent', 'budgeted')
|
'budgets.index', compact(
|
||||||
|
'budgetMaximum', 'periodStart', 'periodEnd',
|
||||||
|
'period', 'range', 'budgetIncomeTotal',
|
||||||
|
'defaultCurrency', 'inactive', 'budgets',
|
||||||
|
'spent', 'budgeted'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -408,14 +408,16 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
|
* @param string $repeatFreq
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return LimitRepetition
|
* @return LimitRepetition
|
||||||
*/
|
*/
|
||||||
public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end): LimitRepetition
|
public function getCurrentRepetition(Budget $budget, string $repeatFreq, Carbon $start, Carbon $end): LimitRepetition
|
||||||
{
|
{
|
||||||
$data = $budget->limitrepetitions()
|
$data = $budget->limitrepetitions()
|
||||||
|
->where('budget_limits.repeat_freq', $repeatFreq)
|
||||||
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
|
->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00'))
|
||||||
->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00'))
|
->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00'))
|
||||||
->first(['limit_repetitions.*']);
|
->first(['limit_repetitions.*']);
|
||||||
@@ -844,4 +846,44 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
|||||||
|
|
||||||
return $limit;
|
return $limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of budget limits that are valid in the current given range.
|
||||||
|
* $ignore is optional. Send an empty limit rep.
|
||||||
|
*
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
* @param LimitRepetition $ignore
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getValidRepetitions(Budget $budget, Carbon $start, Carbon $end, LimitRepetition $ignore) : Collection
|
||||||
|
{
|
||||||
|
$query = $budget->limitrepetitions()
|
||||||
|
->where( // valid when either of these are true:
|
||||||
|
function ($q) use ($start, $end) {
|
||||||
|
$q->where(
|
||||||
|
function ($query) use ($start, $end) {
|
||||||
|
// starts before start time, and the end also after start time.
|
||||||
|
$query->where('limit_repetitions.startdate', '<=', $start->format('Y-m-d 00:00:00'));
|
||||||
|
$query->where('limit_repetitions.enddate', '>=', $start->format('Y-m-d 00:00:00'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$q->orWhere(
|
||||||
|
function ($query) use ($start, $end) {
|
||||||
|
// end after end time, and start is before end time
|
||||||
|
$query->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00'));
|
||||||
|
$query->where('limit_repetitions.enddate', '>=', $end->format('Y-m-d 00:00:00'));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!is_null($ignore->id)) {
|
||||||
|
$query->where('limit_repetitions.id', '!=', $ignore->id);
|
||||||
|
}
|
||||||
|
$data = $query->get(['limit_repetitions.*']);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -146,14 +146,27 @@ interface BudgetRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function getBudgetsAndLimitsInRange(Carbon $start, Carbon $end): Collection;
|
public function getBudgetsAndLimitsInRange(Carbon $start, Carbon $end): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of budget limits that are valid in the current given range.
|
||||||
|
*
|
||||||
|
* @param Budget $budget
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
* @param LimitRepetition $ignore
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getValidRepetitions(Budget $budget, Carbon $start, Carbon $end, LimitRepetition $ignore) : Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
|
* @param string $repeatFreq
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return LimitRepetition
|
* @return LimitRepetition
|
||||||
*/
|
*/
|
||||||
public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end): LimitRepetition;
|
public function getCurrentRepetition(Budget $budget, string $repeatFreq, Carbon $start, Carbon $end): LimitRepetition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all expenses for the given budget and the given accounts, in the given period.
|
* Returns all expenses for the given budget and the given accounts, in the given period.
|
||||||
|
@@ -459,8 +459,11 @@ return [
|
|||||||
'store_new_budget' => 'Store new budget',
|
'store_new_budget' => 'Store new budget',
|
||||||
'stored_new_budget' => 'Stored new budget ":name"',
|
'stored_new_budget' => 'Stored new budget ":name"',
|
||||||
'availableIn' => 'Available in :date',
|
'availableIn' => 'Available in :date',
|
||||||
|
'available_between' => 'Available between :start and :end',
|
||||||
'transactionsWithoutBudget' => 'Expenses without budget',
|
'transactionsWithoutBudget' => 'Expenses without budget',
|
||||||
'transactionsWithoutBudgetDate' => 'Expenses without budget in :date',
|
'transactionsWithoutBudgetDate' => 'Expenses without budget in :date',
|
||||||
|
'transactions_no_budget' => 'Expenses without budget between :start and :end',
|
||||||
|
'spent_between' => 'Spent between :start and :end',
|
||||||
'createBudget' => 'New budget',
|
'createBudget' => 'New budget',
|
||||||
'inactiveBudgets' => 'Inactive budgets',
|
'inactiveBudgets' => 'Inactive budgets',
|
||||||
'without_budget_between' => 'Transactions without a budget between :start and :end',
|
'without_budget_between' => 'Transactions without a budget between :start and :end',
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<div class="col-lg-9 col-sm-8 col-md-8">
|
<div class="col-lg-9 col-sm-8 col-md-8">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header with-border">
|
<div class="box-header with-border">
|
||||||
<h3 class="box-title">{{ Session.get('start').formatLocalized(monthFormat) }}</h3>
|
<h3 class="box-title">{{ periodStart }} — {{ periodEnd }}</h3>
|
||||||
|
|
||||||
<!-- ACTIONS MENU -->
|
<!-- ACTIONS MENU -->
|
||||||
<div class="box-tools pull-right">
|
<div class="box-tools pull-right">
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<small>{{ 'budgeted'|_ }}: <span id="budgetedAmount" class="text-success">{{ budgeted|formatAmountPlain }}</span></small>
|
<small>{{ 'budgeted'|_ }}: <span id="budgetedAmount" class="text-success">{{ budgeted|formatAmountPlain }}</span></small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6 col-md-4 col-sm-3" style="text-align:right;">
|
<div class="col-lg-6 col-md-4 col-sm-3" style="text-align:right;">
|
||||||
<small>{{ trans('firefly.availableIn',{date : period }) }}:
|
<small>{{ trans('firefly.available_between',{start : periodStart, end: periodEnd }) }}:
|
||||||
<a href="#" class="updateIncome"><span id="budgetIncomeTotal"
|
<a href="#" class="updateIncome"><span id="budgetIncomeTotal"
|
||||||
data-value="{{ budgetIncomeTotal }}">{{ budgetIncomeTotal|formatAmount }}</span></a>
|
data-value="{{ budgetIncomeTotal }}">{{ budgetIncomeTotal|formatAmount }}</span></a>
|
||||||
</small>
|
</small>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6 col-md-4 col-sm-3">
|
<div class="col-lg-6 col-md-4 col-sm-3">
|
||||||
<small>{{ 'spent'|_ }}: {{ spent|formatAmount }}</small>
|
<small>{{ trans('firefly.spent_between', {start: periodStart, end: periodEnd}) }}: {{ spent|formatAmount }}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ route('budgets.noBudget') }}">
|
<a href="{{ route('budgets.noBudget') }}">
|
||||||
{{ trans('firefly.transactionsWithoutBudgetDate', {date: period|lower }) }}
|
{{ trans('firefly.transactions_no_budget', {start: periodStart, end: periodEnd }) }}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -99,7 +99,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Bla bla bla</p>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for budget in budgets %}
|
{% for budget in budgets %}
|
||||||
<div class="col-lg-4 col-sm-6 col-md-6">
|
<div class="col-lg-4 col-sm-6 col-md-6">
|
||||||
@@ -132,7 +131,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="width:40%;">
|
<td style="width:40%;">
|
||||||
{{ 'budgeted'|_ }}
|
{{ 'budgeted'|_ }}
|
||||||
<span class="small"><br />
|
<span class="small"><br/>
|
||||||
{{ session('start').formatLocalized(monthAndDayFormat) }} -
|
{{ session('start').formatLocalized(monthAndDayFormat) }} -
|
||||||
{{ session('end').formatLocalized(monthAndDayFormat) }}</span>
|
{{ session('end').formatLocalized(monthAndDayFormat) }}</span>
|
||||||
</td>
|
</td>
|
||||||
@@ -160,35 +159,43 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="width:40%;">
|
<td style="width:40%;">
|
||||||
{{ 'spent'|_ }}
|
{{ 'spent'|_ }}
|
||||||
<span class="small"><br />
|
<span class="small"><br/>
|
||||||
{{ session('start').formatLocalized(monthAndDayFormat) }} -
|
{{ session('start').formatLocalized(monthAndDayFormat) }} -
|
||||||
{{ session('end').formatLocalized(monthAndDayFormat) }}
|
{{ session('end').formatLocalized(monthAndDayFormat) }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ budget.spent|formatAmount }}</a></td>
|
<td>{{ budget.spent|formatAmount }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<!--
|
{% if budget.otherRepetitions.count > 0 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:40%">
|
<td colspan="2">
|
||||||
<span class="text-danger">
|
<ul class="list-unstyled">
|
||||||
Conflicts
|
{% for other in budget.otherRepetitions %}
|
||||||
</span>
|
{% if other.id != budget.currentRep.id %}
|
||||||
</td>
|
<li>Budgeted
|
||||||
<td>
|
<a href="{{ route('budgets.show', [budget.id, other.id]) }}">{{ other.amount|formatAmountPlain }}</a>
|
||||||
<ul class="list-unstyled">
|
between
|
||||||
<li>Also budgeted {{ 123|formatAmount }} between x and y.</li>
|
{{ other.startdate.formatLocalized(monthAndDayFormat) }}
|
||||||
<li>Also budgeted {{ 123|formatAmount }} between x and y.</li>
|
and {{ other.enddate.formatLocalized(monthAndDayFormat) }}.
|
||||||
</ul>
|
</li>
|
||||||
</td>
|
{% endif %}
|
||||||
</tr>
|
{% endfor %}
|
||||||
-->
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if loop.index % 3 == 0 %}
|
||||||
|
</div><div class="row">
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if inactive|length > 0 %}
|
{% if inactive|length > 0 %}
|
||||||
|
<div class="row">
|
||||||
<div class="col-lg-3 col-sm-4 col-md-6">
|
<div class="col-lg-3 col-sm-4 col-md-6">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header with-border">
|
<div class="box-header with-border">
|
||||||
@@ -209,8 +216,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
Reference in New Issue
Block a user