mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-12 15:35:15 +00:00
Allow user to set multi-currency available budget. WIP
This commit is contained in:
124
app/Http/Controllers/Budget/BudgetLimitController.php
Normal file
124
app/Http/Controllers/Budget/BudgetLimitController.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* BudgetLimitController.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Budget;
|
||||
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class BudgetLimitController
|
||||
*/
|
||||
class BudgetLimitController extends Controller
|
||||
{
|
||||
|
||||
/** @var AvailableBudgetRepositoryInterface */
|
||||
private $abRepository;
|
||||
/** @var BudgetLimitRepositoryInterface */
|
||||
private $blRepository;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var OperationsRepositoryInterface */
|
||||
private $opsRepository;
|
||||
/** @var BudgetRepositoryInterface The budget repository */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* AmountController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', (string)trans('firefly.budgets'));
|
||||
app('view')->share('mainTitleIcon', 'fa-tasks');
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param BudgetLimit $budgetLimit
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function delete(Request $request, BudgetLimit $budgetLimit)
|
||||
{
|
||||
$this->blRepository->destroyBudgetLimit($budgetLimit);
|
||||
session()->flash('success', trans('firefly.deleted_bl'));
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$limit = $this->blRepository->store(
|
||||
[
|
||||
'budget_id' => $request->get('budget_id'),
|
||||
'transaction_currency_id' => $request->get('transaction_currency_id'),
|
||||
'start_date' => $request->get('start'),
|
||||
'end_date' => $request->get('end'),
|
||||
'amount' => $request->get('amount'),
|
||||
]
|
||||
);
|
||||
|
||||
return response()->json($limit->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param BudgetLimit $budgetLimit
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(Request $request, BudgetLimit $budgetLimit): JsonResponse
|
||||
{
|
||||
$amount = $request->get('amount');
|
||||
|
||||
return response()->json($this->blRepository->update($budgetLimit, ['amount' => $amount])->toArray());
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -27,6 +27,8 @@ namespace FireflyIII\Http\Controllers\Budget;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||
@@ -36,7 +38,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -101,17 +103,15 @@ class IndexController extends Controller
|
||||
$range = app('preferences')->get('viewRange', '1M')->data;
|
||||
$start = $start ?? session('start', Carbon::now()->startOfMonth());
|
||||
$end = $end ?? app('navigation')->endOfPeriod($start, $range);
|
||||
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
|
||||
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$budgeted = '0';
|
||||
$spent = '0';
|
||||
|
||||
|
||||
// new period stuff:
|
||||
$periodTitle = app('navigation')->periodShow($start, $range);
|
||||
|
||||
// loop of previous periods:
|
||||
$prevLoop = $this->getPreviousPeriods($start, $range);
|
||||
$nextLoop = $this->getNextPeriods($start, $range);
|
||||
$prevLoop = $this->getPreviousPeriods($start, $range);
|
||||
$nextLoop = $this->getNextPeriods($start, $range);
|
||||
|
||||
// get all available budgets.
|
||||
$ab = $this->abRepository->get($start, $end);
|
||||
@@ -152,17 +152,46 @@ class IndexController extends Controller
|
||||
|
||||
// get all budgets, and paginate them into $budgets.
|
||||
$collection = $this->repository->getActiveBudgets();
|
||||
$total = $collection->count();
|
||||
$budgets = $collection->slice(($page - 1) * $pageSize, $pageSize);
|
||||
$budgets = [];
|
||||
|
||||
// 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'] = [];
|
||||
$budgetLimits = $this->blRepository->getBudgetLimits($current, $start, $end);
|
||||
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($budgetLimits as $limit) {
|
||||
$array['budgeted'][] = [
|
||||
'id' => $limit->id,
|
||||
'amount' => $limit->amount,
|
||||
'currency_id' => $limit->transactionCurrency->id,
|
||||
'currency_symbol' => $limit->transactionCurrency->symbol,
|
||||
'currency_name' => $limit->transactionCurrency->name,
|
||||
'currency_decimal_places' => $limit->transactionCurrency->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;
|
||||
}
|
||||
|
||||
// get all inactive budgets, and simply list them:
|
||||
$inactive = $this->repository->getInactiveBudgets();
|
||||
|
||||
|
||||
// paginate budgets
|
||||
$paginator = new LengthAwarePaginator($budgets, $total, $pageSize, $page);
|
||||
$paginator->setPath(route('budgets.index'));
|
||||
|
||||
return view(
|
||||
'budgets.index', compact(
|
||||
'availableBudgets',
|
||||
@@ -171,11 +200,12 @@ class IndexController extends Controller
|
||||
//'prevText', 'previousLoop', 'nextLoop',
|
||||
'budgeted', 'spent',
|
||||
'prevLoop', 'nextLoop',
|
||||
'paginator',
|
||||
'budgets',
|
||||
'currencies',
|
||||
'enableAddButton',
|
||||
'periodTitle',
|
||||
'defaultCurrency',
|
||||
'page', 'activeDaysPassed', 'activeDaysLeft',
|
||||
'activeDaysPassed', 'activeDaysLeft',
|
||||
'inactive', 'budgets', 'start', 'end'
|
||||
)
|
||||
);
|
||||
|
@@ -70,7 +70,7 @@ class BudgetLimit extends Model
|
||||
];
|
||||
|
||||
/** @var array Fields that can be filled */
|
||||
protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount'];
|
||||
protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount','transaction_currency_id'];
|
||||
|
||||
/**
|
||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||
|
@@ -40,7 +40,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property string $name
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\BudgetLimit[] $budgetLimits
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions
|
||||
|
@@ -227,14 +227,14 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
$q1->where(
|
||||
static function (Builder $q2) use ($start, $end) {
|
||||
$q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00'));
|
||||
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 00:00:00'));
|
||||
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 23:59:59'));
|
||||
}
|
||||
)
|
||||
// budget limit start within period
|
||||
->orWhere(
|
||||
static function (Builder $q3) use ($start, $end) {
|
||||
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00'));
|
||||
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 00:00:00'));
|
||||
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 23:59:59'));
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -242,7 +242,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
->orWhere(
|
||||
static function (Builder $q4) use ($start, $end) {
|
||||
// or start is before start AND end is after end.
|
||||
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 00:00:00'));
|
||||
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 23:59:59'));
|
||||
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00'));
|
||||
}
|
||||
);
|
||||
@@ -265,6 +265,17 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
*
|
||||
* @return BudgetLimit
|
||||
*/
|
||||
public function store(array $data): BudgetLimit
|
||||
{
|
||||
return BudgetLimit::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return BudgetLimit
|
||||
* @deprecated
|
||||
*/
|
||||
public function storeBudgetLimit(array $data): BudgetLimit
|
||||
{
|
||||
/** @var Budget $budget */
|
||||
@@ -303,12 +314,27 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param array $data
|
||||
*
|
||||
* @return BudgetLimit
|
||||
*/
|
||||
public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit
|
||||
{
|
||||
$budgetLimit->amount = $data['amount'] ?? $budgetLimit->amount;
|
||||
$budgetLimit->save();
|
||||
|
||||
return $budgetLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param array $data
|
||||
*
|
||||
* @return BudgetLimit
|
||||
* @throws Exception
|
||||
* @deprecated
|
||||
*/
|
||||
public function updateBudgetLimit(BudgetLimit $budgetLimit, array $data): BudgetLimit
|
||||
{
|
||||
|
@@ -35,6 +35,19 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
interface BudgetLimitRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Tells you which amount has been budgeted (for the given budgets)
|
||||
* in the selected query. Returns a positive amount as a string.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Collection|null $budgets
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function budgeted(Carbon $start, Carbon $end, TransactionCurrency $currency, ?Collection $budgets = null): string;
|
||||
|
||||
/**
|
||||
* Destroy a budget limit.
|
||||
*
|
||||
@@ -80,20 +93,15 @@ interface BudgetLimitRepositoryInterface
|
||||
*
|
||||
* @return BudgetLimit
|
||||
*/
|
||||
public function storeBudgetLimit(array $data): BudgetLimit;
|
||||
public function store(array $data): BudgetLimit;
|
||||
|
||||
/**
|
||||
* Tells you which amount has been budgeted (for the given budgets)
|
||||
* in the selected query. Returns a positive amount as a string.
|
||||
* @param array $data
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Collection|null $budgets
|
||||
*
|
||||
* @return string
|
||||
* @return BudgetLimit
|
||||
* @deprecated
|
||||
*/
|
||||
public function budgeted(Carbon $start, Carbon $end, TransactionCurrency $currency, ?Collection $budgets = null): string;
|
||||
public function storeBudgetLimit(array $data): BudgetLimit;
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
@@ -101,6 +109,15 @@ interface BudgetLimitRepositoryInterface
|
||||
*
|
||||
* @return BudgetLimit
|
||||
*/
|
||||
public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit;
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param array $data
|
||||
*
|
||||
* @return BudgetLimit
|
||||
* @deprecated
|
||||
*/
|
||||
public function updateBudgetLimit(BudgetLimit $budgetLimit, array $data): BudgetLimit;
|
||||
|
||||
/**
|
||||
|
98
public/v1/js/ff/budgets/index.js
vendored
98
public/v1/js/ff/budgets/index.js
vendored
@@ -30,10 +30,15 @@ $(function () {
|
||||
On start, fill the "spent"-bar using the content from the page.
|
||||
*/
|
||||
//drawSpentBar();
|
||||
drawSpentBars();
|
||||
//drawBudgetedBar();
|
||||
|
||||
$('.update_ab').on('click', updateAvailableBudget)
|
||||
$('.create_ab_alt').on('click', createAltAvailableBudget)
|
||||
drawBudgetedBars();
|
||||
|
||||
$('.update_ab').on('click', updateAvailableBudget);
|
||||
$('.create_ab_alt').on('click', createAltAvailableBudget);
|
||||
|
||||
$('.budget_amount').on('change', updateBudgetedAmount);
|
||||
|
||||
/*
|
||||
When the input changes, update the percentages for the budgeted bar:
|
||||
@@ -78,6 +83,38 @@ $(function () {
|
||||
}
|
||||
});
|
||||
|
||||
function updateBudgetedAmount(e) {
|
||||
var input = $(e.currentTarget);
|
||||
var budgetId = parseInt(input.data('id'));
|
||||
var budgetLimitId = parseInt(input.data('limit'));
|
||||
var currencyId = parseInt(input.data('currency'));
|
||||
console.log(budgetLimitId);
|
||||
if (0 === budgetLimitId) {
|
||||
$.post(createBudgetLimitUri, {
|
||||
_token: token,
|
||||
budget_id: budgetId,
|
||||
transaction_currency_id: currencyId,
|
||||
amount: input.val(),
|
||||
start: periodStart,
|
||||
end: periodEnd
|
||||
}).done(function (data) {
|
||||
|
||||
alert('done!');
|
||||
}).fail(function () {
|
||||
alert('I failed :(');
|
||||
});
|
||||
} else {
|
||||
$.post(updateBudgetLimitUri.replace('REPLACEME', budgetLimitId), {
|
||||
_token: token,
|
||||
amount: input.val(),
|
||||
}).done(function (data) {
|
||||
alert('done!');
|
||||
}).fail(function () {
|
||||
alert('I failed :(');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var fixHelper = function (e, tr) {
|
||||
"use strict";
|
||||
var $originals = tr.children();
|
||||
@@ -110,13 +147,15 @@ function sortStop(event, ui) {
|
||||
};
|
||||
$.post('budgets/reorder', arr);
|
||||
}
|
||||
|
||||
function createAltAvailableBudget(e) {
|
||||
var button = $(e.currentTarget);
|
||||
$('#defaultModal').empty().load(createAltAvailableBudgetUri, function () {
|
||||
$('#defaultModal').modal('show');
|
||||
});
|
||||
$('#defaultModal').empty().load(createAltAvailableBudgetUri, function () {
|
||||
$('#defaultModal').modal('show');
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateAvailableBudget(e) {
|
||||
var button = $(e.currentTarget);
|
||||
var abId = parseInt(button.data('id'));
|
||||
@@ -135,6 +174,55 @@ function updateAvailableBudget(e) {
|
||||
}
|
||||
|
||||
|
||||
function drawBudgetedBars() {
|
||||
"use strict";
|
||||
$.each($('.budgeted_bar'), function (i, v) {
|
||||
var bar = $(v);
|
||||
var budgeted = parseFloat(bar.data('budgeted'));
|
||||
var available = parseFloat(bar.data('available'));
|
||||
console.log('Budgeted bar for bar ' + bar.data('id'));
|
||||
var budgetedTooMuch = budgeted > available;
|
||||
var pct;
|
||||
if (budgetedTooMuch) {
|
||||
console.log('over budget');
|
||||
// budgeted too much.
|
||||
pct = (available / budgeted) * 100;
|
||||
bar.find('.progress-bar-warning').css('width', pct + '%');
|
||||
bar.find('.progress-bar-danger').css('width', (100 - pct) + '%');
|
||||
bar.find('.progress-bar-info').css('width', 0);
|
||||
} else {
|
||||
console.log('under budget');
|
||||
pct = (budgeted / available) * 100;
|
||||
bar.find('.progress-bar-warning').css('width', 0);
|
||||
bar.find('.progress-bar-danger').css('width', 0);
|
||||
bar.find('.progress-bar-info').css('width', pct + '%');
|
||||
}
|
||||
//$('#budgetedAmount').html(currencySymbol + ' ' + budgeted.toFixed(2));
|
||||
});
|
||||
}
|
||||
|
||||
function drawSpentBars() {
|
||||
"use strict";
|
||||
$.each($('.spent_bar'), function (i, v) {
|
||||
var bar = $(v);
|
||||
var spent = parseFloat(bar.data('spent')) * -1;
|
||||
var budgeted = parseFloat(bar.data('budgeted'));
|
||||
var overspent = spent > budgeted;
|
||||
var pct;
|
||||
|
||||
if (overspent) {
|
||||
// draw overspent bar
|
||||
pct = (budgeted / spent) * 100;
|
||||
bar.find('.progress-bar-warning').css('width', pct + '%');
|
||||
bar.find('.progress-bar-danger').css('width', (100 - pct) + '%');
|
||||
} else {
|
||||
// draw normal bar:
|
||||
pct = (spent / budgeted) * 100;
|
||||
bar.find('.progress-bar-info').css('width', pct + '%');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// function drawSpentBar() {
|
||||
|
@@ -688,7 +688,9 @@ return [
|
||||
'set_ab' => 'The available budget amount has been set',
|
||||
'updated_ab' => 'The available budget amount has been updated',
|
||||
'deleted_ab' => 'The available budget amount has been deleted',
|
||||
'deleted_bl' => 'The budgeted amount has been removed',
|
||||
'alt_currency_ab_create' => 'Set the available budget in another currency',
|
||||
'bl_create_btn' => 'Set budget in another currency',
|
||||
'inactiveBudgets' => 'Inactive budgets',
|
||||
'without_budget_between' => 'Transactions without a budget between :start and :end',
|
||||
'delete_budget' => 'Delete budget ":name"',
|
||||
|
@@ -145,7 +145,8 @@
|
||||
{# progresss bar to visualise available vs budgeted. #}
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="progress budgetedBar" data-id="{{ budget.id }}">
|
||||
<div class="progress budgeted_bar" data-id="{{ budget.id }}" data-budgeted="{{ budget.budgeted }}"
|
||||
data-available="{{ budget.amount }}">
|
||||
<div class="progress-bar progress-bar-danger" data-id="{{ budget.id }}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
@@ -169,7 +170,8 @@
|
||||
{# bar to visualise spending in budget .#}
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="progress spentBar" data-id="{{ budget.id }}">
|
||||
<div class="progress spent_bar" data-id="{{ budget.id }}" data-budgeted="{{ budget.budgeted }}"
|
||||
data-spent="{{ budget.spent }}">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="0" aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
style="width: 0;"></div>
|
||||
@@ -195,63 +197,12 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{#
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
|
||||
<small>{{ 'budgeted'|_ }}: <span id="budgetedAmount"
|
||||
class="text-success">{{ "-1"|formatAmountPlain }}{{ 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: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat) }) }}
|
||||
:
|
||||
<span id="available" data-value="{{ available }}">{{ "-1"|formatAmountPlain }}{{ available|formatAmountPlain }}</span>
|
||||
<a href="#" class="updateIncome btn btn-default btn-xs"><i class="fa fa-pencil"></i></a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="progress budgetedBar">
|
||||
<div class="progress-bar progress-bar-danger" id="progress-bar-danger" role="progressbar" aria-valuenow="0" aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-warning" id="progress-bar-warning" role="progressbar" aria-valuenow="10" aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-info" id="progress-bar-default" role="progressbar" aria-valuenow="0" aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</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: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}
|
||||
: {{ "-1"|formatAmount }}{{ spent|formatAmount }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="progress spentBar">
|
||||
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
style="width: 0;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>#}
|
||||
{% if paginator.count == 0 and inactive.count == 0 and page == 1 %}
|
||||
{% if budgets.count == 0 and inactive.count == 0 %}
|
||||
{% include 'partials.empty' with {objectType: 'default', type: 'budgets',route: route('budgets.create')} %}
|
||||
{# make FF ignore demo for now. #}
|
||||
{% set shownDemo = true %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="box">
|
||||
@@ -262,9 +213,6 @@
|
||||
<div style="padding:8px;">
|
||||
<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;">
|
||||
{{ paginator.render|raw }}
|
||||
</div>
|
||||
<table class="table table-bordered sortable-table table-striped sortable" id="budgetList">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -276,64 +224,93 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for budget in paginator %}
|
||||
{% for budget in budgets %}
|
||||
<tr data-id="{{ budget.id }}">
|
||||
<td class="hidden-sm hidden-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="{{ 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.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>
|
||||
</div>
|
||||
</td>
|
||||
<td data-value="{{ budget.name }}">
|
||||
|
||||
{% if budgetInformation[budget.id]['currentLimit'] %}
|
||||
<a href="{{ route('budgets.show.limit', [budget.id, budgetInformation[budget.id]['currentLimit'].id]) }}"
|
||||
class="budget-link"
|
||||
data-id="{{ budget.id }}">{{ budget.name }}</a>
|
||||
{% else %}
|
||||
<a href="{{ route('budgets.show',budget.id) }}" class="budget-link" data-id="{{ budget.id }}">{{ budget.name }}</a>
|
||||
<a href="{{ route('budgets.show',budget.id) }}" data-id="{{ budget.id }}">{{ budget.name }}</a>
|
||||
</td>
|
||||
<td data-value="-1">
|
||||
{% if 0==budget.budgeted|length %}
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">{{ defaultCurrency.symbol }}</div>
|
||||
<input type="hidden" name="balance_currency_id" value="{{ defaultCurrency.id }}"/>
|
||||
<input class="form-control budget_amount" data-original="0" data-id="{{ budget.id }}"
|
||||
data-currency="{{ defaultCurrency.id }}" data-limit="0" value="0" autocomplete="off" min="0" name="amount"
|
||||
type="number">
|
||||
</div>
|
||||
<span class="text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
|
||||
style="display:none;"></span>
|
||||
{% endif %}
|
||||
{% if budget.budgeted|length > 0 %}
|
||||
{% for budgetLimit in budget.budgeted %}
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">{{ budgetLimit.currency_symbol }}</div>
|
||||
<input class="form-control budget_amount" data-original="{{ budgetLimit.amount }}"
|
||||
data-id="{{ budget.id }}" data-limit="{{ budgetLimit.id }}" value="{{ budgetLimit.amount }}"
|
||||
autocomplete="off"
|
||||
min="0" name="amount" type="number">
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="false"><span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ route('budget-limits.delete', [budgetLimit.id]) }}">Remove budgeted amount
|
||||
in {{ budgetLimit.currency_name }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
|
||||
style="display:none;"></span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if budget.budgeted|length < currencies.count %}
|
||||
<a href="#" class="btn btn-success btn-xs create_ab_alt">
|
||||
<i class="fa fa-plus-circle"></i>
|
||||
{{ 'bl_create_btn'|_ }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if budgetInformation[budget.id]['currentLimit'] %}
|
||||
{% set repAmount = budgetInformation[budget.id]['budgeted'] %}
|
||||
{% else %}
|
||||
{% set repAmount = '0' %}
|
||||
{% endif %}
|
||||
<td class="hidden-sm hidden-xs spent" data-id="{{ budget.id }}">
|
||||
{% for spentInfo in budget.spent %}
|
||||
{{ formatAmountBySymbol(spentInfo.spent, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}
|
||||
({{ formatAmountBySymbol(spentInfo.spent / activeDaysPassed, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
|
||||
{% endfor %}
|
||||
|
||||
<td data-value="{{ repAmount }}">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">{{ defaultCurrency.symbol|raw }}</div>
|
||||
<input type="hidden" name="balance_currency_id" value="{{ defaultCurrency.id }}"/>
|
||||
<input class="form-control budgetAmount" data-original="{{ repAmount }}"
|
||||
data-id="{{ budget.id }}" value="{{ repAmount }}" autocomplete="off"
|
||||
min="0" name="amount" type="number">
|
||||
</div>
|
||||
<span class="text-danger budget_warning" data-id="{{ budget.id }}" style="display:none;"></span>
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs spent" data-id="{{ budget.id }}" data-spent="{{ budgetInformation[budget.id]['spent'] }}"
|
||||
data-value="{{ budgetInformation[budget.id]['spent'] }}">
|
||||
{{ "-1"|formatAmount }}
|
||||
{#{{ budgetInformation[budget.id]['spent']|formatAmount }}#}
|
||||
({{ "-1"|formatAmount }})
|
||||
{#({{ (budgetInformation[budget.id]['spent'] / activeDaysPassed)|formatAmount }})#}
|
||||
</td>
|
||||
<td class="left" data-id="{{ budget.id }}"
|
||||
data-value="{{ (repAmount + budgetInformation[budget.id]['spent']) }}">
|
||||
{{ "-1"|formatAmount }}
|
||||
<td class="left" data-id="{{ budget.id }}">
|
||||
<!-- only makes sense to list what's left for budgeted amounts.-->
|
||||
{% if budget.budgeted|length > 0 %}
|
||||
{% for budgetLimit in budget.budgeted %}
|
||||
{% for spentInfo in budget.spent %}
|
||||
{% if spentInfo.currency_id == budgetLimit.currency_id %}
|
||||
{{ formatAmountBySymbol(spentInfo.spent + budgetLimit.amount, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}
|
||||
{% if spentInfo.spent + budgetLimit.amount > 0 %}
|
||||
({{ formatAmountBySymbol((spentInfo.spent + budgetLimit.amount) / activeDaysLeft, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
|
||||
{% else %}
|
||||
({{ formatAmountBySymbol(0, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% 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 %}
|
||||
{#{% if repAmount + budgetInformation[budget.id]['spent'] > 0 %}#}
|
||||
{#({{ "-1"|formatAmount }})#}
|
||||
{#({{ ((repAmount + budgetInformation[budget.id]['spent']) / activeDaysLeft)|formatAmount }})#}
|
||||
{#{% endif %}#}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="padding-left:8px;">
|
||||
{{ paginator.render|raw }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<a href="{{ route('budgets.create') }}" class="btn btn-success"><i class="fa fa-plus fa-fw"></i> {{ 'createBudget'|_ }}</a>
|
||||
@@ -399,16 +376,14 @@
|
||||
{% block scripts %}
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
// actually spent bar data:
|
||||
var spent = 0;
|
||||
var page = {{ page }};
|
||||
// budgeted data:
|
||||
var budgeted = 0;
|
||||
var available = 0;
|
||||
var budgetIndexUri = "{{ route('budgets.index',['START','END']) }}";
|
||||
var createAvailableBudgetUri = "{{ route('available-budgets.create', [start.format('Y-m-d'), end.format('Y-m-d')]) }}";
|
||||
var createAltAvailableBudgetUri = "{{ route('available-budgets.create-alternative', [start.format('Y-m-d'), end.format('Y-m-d')]) }}";
|
||||
var editAvailableBudgetUri = "{{ route('available-budgets.edit', ['REPLACEME']) }}";
|
||||
|
||||
var createBudgetLimitUri = "{{ route('budget-limits.store') }}";
|
||||
var updateBudgetLimitUri = "{{ route('budget-limits.update', ['REPLACEME']) }}";
|
||||
|
||||
{#var budgetAmountUri = "{{ route('budgets.amount','REPLACE') }}";#}
|
||||
{#var updateIncomeUri = "{{ route('budgets.income',[start.format('Y-m-d'),end.format('Y-m-d')]) }}?page={{ page }}";#}
|
||||
var periodStart = "{{ start.format('Y-m-d') }}";
|
||||
|
@@ -117,7 +117,9 @@ Route::group(
|
||||
Route::get('{objectType}', ['uses' => 'Account\IndexController@index', 'as' => 'index'])->where('objectType', 'revenue|asset|expense|liabilities');
|
||||
|
||||
// create
|
||||
Route::get('create/{objectType}', ['uses' => 'Account\CreateController@create', 'as' => 'create'])->where('objectType', 'revenue|asset|expense|liabilities');
|
||||
Route::get('create/{objectType}', ['uses' => 'Account\CreateController@create', 'as' => 'create'])->where(
|
||||
'objectType', 'revenue|asset|expense|liabilities'
|
||||
);
|
||||
Route::post('store', ['uses' => 'Account\CreateController@store', 'as' => 'store']);
|
||||
|
||||
|
||||
@@ -230,32 +232,36 @@ Route::group(
|
||||
* Available Budget Controller
|
||||
*/
|
||||
Route::group(
|
||||
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers', 'prefix' => 'available-budgets', 'as' => 'available-budgets.'], function () {
|
||||
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers', 'prefix' => 'available-budgets', 'as' => 'available-budgets.'],
|
||||
function () {
|
||||
|
||||
// delete
|
||||
//Route::get('delete/{budget}', ['uses' => 'Budget\DeleteController@delete', 'as' => 'delete']);
|
||||
//Route::post('destroy/{budget}', ['uses' => 'Budget\DeleteController@destroy', 'as' => 'destroy']);
|
||||
// create
|
||||
Route::get('create/{start_date}/{end_date}/{currency?}', ['uses' => 'Budget\AvailableBudgetController@create', 'as' => 'create']);
|
||||
Route::get(
|
||||
'create-alternative/{start_date}/{end_date}', ['uses' => 'Budget\AvailableBudgetController@createAlternative', 'as' => 'create-alternative']
|
||||
);
|
||||
Route::post('store', ['uses' => 'Budget\AvailableBudgetController@store', 'as' => 'store']);
|
||||
|
||||
// create
|
||||
Route::get('create/{start_date}/{end_date}/{currency?}', ['uses' => 'Budget\AvailableBudgetController@create', 'as' => 'create']);
|
||||
Route::get('create-alternative/{start_date}/{end_date}', ['uses' => 'Budget\AvailableBudgetController@createAlternative', 'as' => 'create-alternative']);
|
||||
Route::post('store', ['uses' => 'Budget\AvailableBudgetController@store', 'as' => 'store']);
|
||||
//Route::post('store', ['uses' => 'Budget\CreateController@store', 'as' => 'store']);
|
||||
// edit
|
||||
Route::get('edit/{availableBudget}', ['uses' => 'Budget\AvailableBudgetController@edit', 'as' => 'edit']);
|
||||
Route::post('update/{availableBudget}', ['uses' => 'Budget\AvailableBudgetController@update', 'as' => 'update']);
|
||||
|
||||
// edit
|
||||
Route::get('edit/{availableBudget}', ['uses' => 'Budget\AvailableBudgetController@edit', 'as' => 'edit']);
|
||||
Route::post('update/{availableBudget}', ['uses' => 'Budget\AvailableBudgetController@update', 'as' => 'update']);
|
||||
Route::get('delete/{availableBudget}', ['uses' => 'Budget\AvailableBudgetController@delete', 'as' => 'delete']);
|
||||
}
|
||||
);
|
||||
|
||||
Route::get('delete/{availableBudget}', ['uses' => 'Budget\AvailableBudgetController@delete', 'as' => 'delete']);
|
||||
//Route::get('edit/{budget}', ['uses' => 'Budget\EditController@edit', 'as' => 'edit']);
|
||||
//Route::post('update/{budget}', ['uses' => 'Budget\EditController@update', 'as' => 'update']);
|
||||
|
||||
// show
|
||||
//Route::get('show/{budget}', ['uses' => 'Budget\ShowController@show', 'as' => 'show']);
|
||||
//Route::get('show/{budget}/{budgetLimit}', ['uses' => 'Budget\ShowController@showByBudgetLimit', 'as' => 'show.limit']);
|
||||
//Route::get('list/no-budget/all', ['uses' => 'Budget\ShowController@noBudgetAll', 'as' => 'no-budget-all']);
|
||||
//Route::get('list/no-budget/{start_date?}/{end_date?}', ['uses' => 'Budget\ShowController@noBudget', 'as' => 'no-budget']);
|
||||
}
|
||||
/**
|
||||
* Budget Limit Controller
|
||||
*/
|
||||
Route::group(
|
||||
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers', 'prefix' => 'budget-limits', 'as' => 'budget-limits.'],
|
||||
static function () {
|
||||
|
||||
Route::get('delete/{budgetLimit}', ['uses' => 'Budget\BudgetLimitController@delete', 'as' => 'delete']);
|
||||
Route::post('store', ['uses' => 'Budget\BudgetLimitController@store', 'as' => 'store']);
|
||||
Route::post('update/{budgetLimit}', ['uses' => 'Budget\BudgetLimitController@update', 'as' => 'update']);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -389,7 +395,9 @@ Route::group(
|
||||
Route::get('period/{category}', ['uses' => 'CategoryController@currentPeriod', 'as' => 'current']);
|
||||
Route::get('period/{category}/{date}', ['uses' => 'CategoryController@specificPeriod', 'as' => 'specific']);
|
||||
Route::get('all/{category}', ['uses' => 'CategoryController@all', 'as' => 'all']);
|
||||
Route::get('report-period/0/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@reportPeriodNoCategory', 'as' => 'period.no-category']);
|
||||
Route::get(
|
||||
'report-period/0/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@reportPeriodNoCategory', 'as' => 'period.no-category']
|
||||
);
|
||||
Route::get('report-period/{category}/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@reportPeriod', 'as' => 'period']);
|
||||
|
||||
// these charts are used in reports (category reports):
|
||||
@@ -574,7 +582,6 @@ Route::group(
|
||||
Route::get('currency-names', ['uses' => 'Json\AutoCompleteController@currencyNames', 'as' => 'autocomplete.currency-names']);
|
||||
|
||||
|
||||
|
||||
Route::get('transaction-types', ['uses' => 'Json\AutoCompleteController@transactionTypes', 'as' => 'transaction-types']);
|
||||
|
||||
// boxes
|
||||
@@ -905,7 +912,9 @@ Route::group(
|
||||
|
||||
// show groups:
|
||||
// TODO improve these routes
|
||||
Route::get('{what}/all', ['uses' => 'Transaction\IndexController@indexAll', 'as' => 'index.all'])->where(['what' => 'withdrawal|deposit|transfers|transfer']);
|
||||
Route::get('{what}/all', ['uses' => 'Transaction\IndexController@indexAll', 'as' => 'index.all'])->where(
|
||||
['what' => 'withdrawal|deposit|transfers|transfer']
|
||||
);
|
||||
|
||||
Route::get('{what}/{start_date?}/{end_date?}', ['uses' => 'Transaction\IndexController@index', 'as' => 'index'])->where(
|
||||
['what' => 'withdrawal|deposit|transfers|transfer']
|
||||
|
Reference in New Issue
Block a user