mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Initial code for #595, transactions with no budget
This commit is contained in:
parent
0e59f7433c
commit
61007a95a6
@ -24,9 +24,12 @@ use FireflyIII\Models\Budget;
|
|||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Log;
|
||||||
|
use Navigation;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
use View;
|
use View;
|
||||||
@ -191,27 +194,66 @@ class BudgetController extends Controller
|
|||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function noBudget(Request $request)
|
public function noBudget(Request $request, string $moment = '')
|
||||||
{
|
{
|
||||||
/** @var Carbon $start */
|
// default values:
|
||||||
$start = session('start', Carbon::now()->startOfMonth());
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
/** @var Carbon $end */
|
$start = null;
|
||||||
$end = session('end', Carbon::now()->endOfMonth());
|
$end = null;
|
||||||
|
$periods = new Collection;
|
||||||
|
|
||||||
|
// prep for "all" view.
|
||||||
|
if ($moment === 'all') {
|
||||||
|
$subTitle = trans('firefly.all_journals_without_budget');
|
||||||
|
}
|
||||||
|
|
||||||
|
// prep for "specific date" view.
|
||||||
|
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||||
|
$start = new Carbon($moment);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.without_budget_between',
|
||||||
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
$periods = $this->noBudgetPeriodEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
// prep for current period
|
||||||
|
if (strlen($moment) === 0) {
|
||||||
|
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||||
|
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||||
|
$periods = $this->noBudgetPeriodEntries();
|
||||||
|
$subTitle = trans(
|
||||||
|
'firefly.without_budget_between',
|
||||||
|
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$subTitle = trans(
|
|
||||||
'firefly.without_budget_between',
|
|
||||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
|
||||||
);
|
|
||||||
|
|
||||||
// collector
|
$count = 0;
|
||||||
/** @var JournalCollectorInterface $collector */
|
$loop = 0;
|
||||||
$collector = app(JournalCollectorInterface::class);
|
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
|
Log::info('Now at no-budget loop start.');
|
||||||
$journals = $collector->getPaginatedJournals();
|
while ($count === 0 && $loop < 3) {
|
||||||
$journals->setPath('/budgets/list/noBudget');
|
$loop++;
|
||||||
|
Log::info('Count is zero, search for journals.');
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget();
|
||||||
|
$journals = $collector->getPaginatedJournals();
|
||||||
|
$journals->setPath('/budgets/list/no-budget');
|
||||||
|
$count = $journals->getCollection()->count();
|
||||||
|
if ($count === 0) {
|
||||||
|
$start->subDay();
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfPeriod($start, $range);
|
||||||
|
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view('budgets.no-budget', compact('journals', 'subTitle'));
|
return view('budgets.no-budget', compact('journals', 'subTitle', 'periods', 'start', 'end'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -405,7 +447,6 @@ class BudgetController extends Controller
|
|||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Budget $budget
|
* @param Budget $budget
|
||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
@ -442,4 +483,47 @@ class BudgetController extends Controller
|
|||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function noBudgetPeriodEntries(): Collection
|
||||||
|
{
|
||||||
|
$repository = app(JournalRepositoryInterface::class);
|
||||||
|
$first = $repository->first();
|
||||||
|
$start = $first->date ?? new Carbon;
|
||||||
|
$range = Preferences::get('viewRange', '1M')->data;
|
||||||
|
$start = Navigation::startOfPeriod($start, $range);
|
||||||
|
$end = Navigation::endOfX(new Carbon, $range);
|
||||||
|
$entries = new Collection;
|
||||||
|
|
||||||
|
// properties for cache
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($end);
|
||||||
|
$cache->addProperty('no-budget-period-entries');
|
||||||
|
|
||||||
|
if ($cache->has()) {
|
||||||
|
return $cache->get(); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug('Going to get period expenses and incomes.');
|
||||||
|
while ($end >= $start) {
|
||||||
|
$end = Navigation::startOfPeriod($end, $range);
|
||||||
|
$currentEnd = Navigation::endOfPeriod($end, $range);
|
||||||
|
|
||||||
|
// count journals without budget in this period:
|
||||||
|
/** @var JournalCollectorInterface $collector */
|
||||||
|
$collector = app(JournalCollectorInterface::class);
|
||||||
|
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutBudget();
|
||||||
|
$journals = $collector->getJournals()->count();
|
||||||
|
$dateStr = $end->format('Y-m-d');
|
||||||
|
$dateName = Navigation::periodShow($end, $range);
|
||||||
|
$entries->push([$dateStr, $dateName, $journals, clone $end]);
|
||||||
|
$end = Navigation::subtractPeriod($end, $range, 1);
|
||||||
|
}
|
||||||
|
$cache->store($entries);
|
||||||
|
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,73 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
{# upper show-all instruction #}
|
||||||
|
{% if periods.count > 0 %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12">
|
||||||
|
<p class="small text-center"><a href="{{ route('budgets.no-budget',['all']) }}">{{ 'showEverything'|_ }}</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
<div class="{% if periods.count > 0 %}col-lg-10 col-md-10 col-sm-12{% else %}col-lg-12 col-md-12 col-sm-12{% endif %}">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header with-border">
|
<div class="box-header with-border">
|
||||||
<h3 class="box-title">{{ subTitle }}</h3>
|
<h3 class="box-title">{{ subTitle }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body ">
|
||||||
{% include 'list.journals-tasker' with {'journals': journals} %}
|
{% include 'list.journals-tasker' with {'journals': journals} %}
|
||||||
|
{% if periods.count > 0 %}
|
||||||
|
<p>
|
||||||
|
<i class="fa fa-calendar" aria-hidden="true"></i>
|
||||||
|
<a href="{{ route('budgets.no-budget', ['all']) }}">{{ 'show_all_no_filter'|_ }}</a>
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
<i class="fa fa-calendar" aria-hidden="true"></i>
|
||||||
|
<a href="{{ route('budgets.no-budget') }}">{{ 'show_the_current_period_and_overview'|_ }}</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if periods.count > 0 %}
|
||||||
|
<div class="col-lg-2 col-md-2 col-sm-12 col-xs-12">
|
||||||
|
{% for entry in periods %}
|
||||||
|
{% if entry[2] > 0 %}
|
||||||
|
<div class="box {% if entry[3] == start %}box-solid box-primary{% endif %}">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title"><a href="{{ route('budgets.no-budget',[entry[0]]) }}">{{ entry[1] }}</a>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body no-padding">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<tr>
|
||||||
|
<td style="width:33%;">{{ 'transactions'|_ }}</td>
|
||||||
|
<td style="text-align: right;">{{ entry[2] }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{# lower show-all instruction #}
|
||||||
|
{% if periods.count > 0 %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12">
|
||||||
|
<p class="small text-center"><a href="{{ route('budgets.no-budget',['all']) }}">{{ 'showEverything'|_ }}</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script type="text/javascript" src="js/ff/transactions/list.js"></script>
|
<script type="text/javascript" src="js/ff/transactions/list.js"></script>
|
||||||
|
@ -144,7 +144,7 @@ Route::group(
|
|||||||
Route::get('delete/{budget}', ['uses' => 'BudgetController@delete', 'as' => 'delete']);
|
Route::get('delete/{budget}', ['uses' => 'BudgetController@delete', 'as' => 'delete']);
|
||||||
Route::get('show/{budget}', ['uses' => 'BudgetController@show', 'as' => 'show']);
|
Route::get('show/{budget}', ['uses' => 'BudgetController@show', 'as' => 'show']);
|
||||||
Route::get('show/{budget}/{budgetlimit}', ['uses' => 'BudgetController@showByBudgetLimit', 'as' => 'show.limit']);
|
Route::get('show/{budget}/{budgetlimit}', ['uses' => 'BudgetController@showByBudgetLimit', 'as' => 'show.limit']);
|
||||||
Route::get('list/no-budget', ['uses' => 'BudgetController@noBudget', 'as' => 'no-budget']);
|
Route::get('list/no-budget/{moment?}', ['uses' => 'BudgetController@noBudget', 'as' => 'no-budget']);
|
||||||
|
|
||||||
Route::post('income', ['uses' => 'BudgetController@postUpdateIncome', 'as' => 'income.post']);
|
Route::post('income', ['uses' => 'BudgetController@postUpdateIncome', 'as' => 'income.post']);
|
||||||
Route::post('store', ['uses' => 'BudgetController@store', 'as' => 'store']);
|
Route::post('store', ['uses' => 'BudgetController@store', 'as' => 'store']);
|
||||||
|
Loading…
Reference in New Issue
Block a user