mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-29 18:20:01 +00:00
Allow user to set multi-currency available budget. WIP
This commit is contained in:
209
app/Http/Controllers/Budget/AvailableBudgetController.php
Normal file
209
app/Http/Controllers/Budget/AvailableBudgetController.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
/**
|
||||
* AvailableBudgetController.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 Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidDateException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
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\Request;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class AvailableBudgetController
|
||||
*/
|
||||
class AvailableBudgetController 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();
|
||||
|
||||
app('view')->share('hideBudgets', true);
|
||||
|
||||
$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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create will always assume the user's default currency, if it's not set.
|
||||
*
|
||||
* This method will check if there is no AB, and refuse to continue if it exists.
|
||||
*/
|
||||
public function create(Request $request, Carbon $start, Carbon $end, ?TransactionCurrency $currency = null)
|
||||
{
|
||||
$currency = $currency ?? app('amount')->getDefaultCurrency();
|
||||
$collection = $this->abRepository->get($start, $end);
|
||||
$filtered = $collection->filter(
|
||||
static function (AvailableBudget $budget) use ($currency) {
|
||||
return $currency->id === $budget->transaction_currency_id;
|
||||
}
|
||||
);
|
||||
if ($filtered->count() > 0) {
|
||||
/** @var AvailableBudget $first */
|
||||
$first = $filtered->first();
|
||||
|
||||
return redirect(route('available-budgets.edit', [$first->id]));
|
||||
}
|
||||
$page = (int)($request->get('page') ?? 1);
|
||||
|
||||
return view('budgets.available-budgets.create', compact('start', 'end', 'page', 'currency'));
|
||||
}
|
||||
|
||||
/**
|
||||
* createAlternative will show a list of enabled currencies so the user can pick one.
|
||||
*/
|
||||
public function createAlternative(Request $request, Carbon $start, Carbon $end)
|
||||
{
|
||||
$currencies = $this->currencyRepos->getEnabled();
|
||||
$availableBudgets = $this->abRepository->get($start, $end);
|
||||
|
||||
// remove already budgeted currencies:
|
||||
$currencies = $currencies->filter(
|
||||
static function (TransactionCurrency $currency) use ($availableBudgets) {
|
||||
/** @var AvailableBudget $budget */
|
||||
foreach ($availableBudgets as $budget) {
|
||||
if ($budget->transaction_currency_id === $currency->id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
$page = (int)($request->get('page') ?? 1);
|
||||
return view('budgets.available-budgets.create-alternative', compact('start', 'end', 'page', 'currencies'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AvailableBudget $availableBudget
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function delete(AvailableBudget $availableBudget)
|
||||
{
|
||||
$this->abRepository->destroyAvailableBudget($availableBudget);
|
||||
session()->flash('success', trans('firefly.deleted_ab'));
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AvailableBudget $availableBudget
|
||||
*/
|
||||
public function edit(AvailableBudget $availableBudget)
|
||||
{
|
||||
return view('budgets.available-budgets.edit', compact('availableBudget'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// make dates.
|
||||
try {
|
||||
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
|
||||
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
|
||||
} catch (InvalidDateException $e) {
|
||||
$start = session()->get('start');
|
||||
$end = session()->get('end');
|
||||
Log::info($e->getMessage());
|
||||
}
|
||||
// find currency
|
||||
$currency = $this->currencyRepos->find((int)$request->get('currency_id'));
|
||||
if (null === $currency) {
|
||||
session()->flash('error', trans('firefly.invalid_currency'));
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
// find existing AB
|
||||
$existing = $this->abRepository->find($currency, $start, $end);
|
||||
if (null === $existing) {
|
||||
$this->abRepository->store(
|
||||
[
|
||||
'amount' => $request->get('amount'),
|
||||
'currency' => $currency,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
]
|
||||
);
|
||||
}
|
||||
if (null !== $existing) {
|
||||
// update amount:
|
||||
$this->abRepository->update($existing, ['amount' => $request->get('amount')]);
|
||||
}
|
||||
session()->flash('success', trans('firefly.set_ab'));
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param AvailableBudget $availableBudget
|
||||
*/
|
||||
public function update(Request $request, AvailableBudget $availableBudget)
|
||||
{
|
||||
$this->abRepository->update($availableBudget, ['amount' => $request->get('amount')]);
|
||||
session()->flash('success', trans('firefly.updated_ab'));
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
}
|
@@ -26,13 +26,18 @@ namespace FireflyIII\Http\Controllers\Budget;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
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 FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -42,12 +47,16 @@ class IndexController extends Controller
|
||||
{
|
||||
|
||||
use DateCalculation;
|
||||
/** @var AvailableBudgetRepositoryInterface */
|
||||
private $abRepository;
|
||||
/** @var BudgetLimitRepositoryInterface */
|
||||
private $blRepository;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepository;
|
||||
/** @var OperationsRepositoryInterface */
|
||||
private $opsRepository;
|
||||
/** @var BudgetRepositoryInterface The budget repository */
|
||||
private $repository;
|
||||
/** @var AvailableBudgetRepositoryInterface */
|
||||
private $abRepository;
|
||||
|
||||
/**
|
||||
* IndexController constructor.
|
||||
@@ -67,6 +76,8 @@ class IndexController extends Controller
|
||||
$this->repository = app(BudgetRepositoryInterface::class);
|
||||
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
||||
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
|
||||
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
||||
$this->repository->cleanupBudgets();
|
||||
|
||||
return $next($request);
|
||||
@@ -93,18 +104,47 @@ class IndexController extends Controller
|
||||
$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);
|
||||
|
||||
// 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);
|
||||
$previousLoop = $this->getPreviousPeriods($start, $range);
|
||||
// loop of previous periods:
|
||||
$prevLoop = $this->getPreviousPeriods($start, $range);
|
||||
$nextLoop = $this->getNextPeriods($start, $range);
|
||||
$currentMonth = app('navigation')->periodShow($start, $range);
|
||||
$nextText = app('navigation')->periodShow($next, $range);
|
||||
$prevText = app('navigation')->periodShow($prev, $range);
|
||||
|
||||
// get all available budgets.
|
||||
$ab = $this->abRepository->get($start, $end);
|
||||
$availableBudgets = [];
|
||||
// for each, complement with spent amount:
|
||||
/** @var AvailableBudget $entry */
|
||||
foreach ($ab as $entry) {
|
||||
$array = $entry->toArray();
|
||||
$array['start_date'] = $entry->start_date;
|
||||
$array['end_date'] = $entry->end_date;
|
||||
|
||||
// spent in period:
|
||||
$spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency);
|
||||
$array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0';
|
||||
|
||||
// budgeted in period:
|
||||
$budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency,);
|
||||
$array['budgeted'] = $budgeted;
|
||||
$availableBudgets[] = $array;
|
||||
unset($spentArr);
|
||||
}
|
||||
|
||||
if (0 === count($availableBudgets)) {
|
||||
// get budgeted for default currency:
|
||||
$budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency,);
|
||||
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, null, $defaultCurrency);
|
||||
$spent = $spentArr[$defaultCurrency->id]['sum'] ?? '0';
|
||||
unset($spentArr);
|
||||
}
|
||||
|
||||
// count the number of enabled currencies. This determines if we display a "+" button.
|
||||
$currencies = $this->currencyRepository->getEnabled();
|
||||
$enableAddButton = $currencies->count() > count($availableBudgets);
|
||||
|
||||
// number of days for consistent budgeting.
|
||||
$activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description.
|
||||
@@ -118,14 +158,6 @@ class IndexController extends Controller
|
||||
// get all inactive budgets, and simply list them:
|
||||
$inactive = $this->repository->getInactiveBudgets();
|
||||
|
||||
// collect budget info to fill bars and so on.
|
||||
$budgetInformation = $this->opsRepository->collectBudgetInformation($collection, $start, $end);
|
||||
|
||||
// to display available budget:
|
||||
$available = $this->abRepository->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);
|
||||
@@ -133,11 +165,18 @@ class IndexController extends Controller
|
||||
|
||||
return view(
|
||||
'budgets.index', compact(
|
||||
'available', 'currentMonth', 'next', 'nextText', 'prev', 'paginator',
|
||||
'prevText',
|
||||
'availableBudgets',
|
||||
//'available',
|
||||
//'currentMonth', 'next', 'nextText', 'prev',
|
||||
//'prevText', 'previousLoop', 'nextLoop',
|
||||
'budgeted', 'spent',
|
||||
'prevLoop', 'nextLoop',
|
||||
'paginator',
|
||||
'enableAddButton',
|
||||
'periodTitle',
|
||||
'defaultCurrency',
|
||||
'page', 'activeDaysPassed', 'activeDaysLeft',
|
||||
'budgetInformation',
|
||||
'inactive', 'budgets', 'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start', 'end'
|
||||
'inactive', 'budgets', 'start', 'end'
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -160,6 +199,7 @@ class IndexController extends Controller
|
||||
$budgetId = (int)$budgetId;
|
||||
$budget = $repository->findNull($budgetId);
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $currentOrder));
|
||||
$repository->setBudgetOrder($budget, $currentOrder);
|
||||
}
|
||||
$currentOrder++;
|
||||
|
@@ -29,6 +29,7 @@ use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
@@ -64,6 +65,48 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find existing AB.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return AvailableBudget|null
|
||||
*/
|
||||
public function find(TransactionCurrency $currency, Carbon $start, Carbon $end): ?AvailableBudget
|
||||
{
|
||||
return $this->user->availableBudgets()
|
||||
->where('transaction_currency_id', $currency->id)
|
||||
->where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $end->format('Y-m-d 00:00:00'))
|
||||
->first();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all available budgets (in all currencies) (for the selected period).
|
||||
*
|
||||
* @param Carbon|null $start
|
||||
* @param Carbon|null $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function get(?Carbon $start = null, ?Carbon $end = null): Collection
|
||||
{
|
||||
$query = $this->user->availableBudgets()->with(['transactionCurrency']);
|
||||
if (null !== $start && null !== $end) {
|
||||
$query->where(
|
||||
static function (Builder $q1) use ($start, $end) {
|
||||
$q1->where('start_date', '=', $start->format('Y-m-d 00:00:00'));
|
||||
$q1->where('end_date', '=', $end->format('Y-m-d 00:00:00'));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $query->get(['available_budgets.*']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Carbon $start
|
||||
@@ -140,7 +183,6 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Carbon $start
|
||||
@@ -176,6 +218,41 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return AvailableBudget|null
|
||||
*/
|
||||
public function store(array $data): ?AvailableBudget
|
||||
{
|
||||
return AvailableBudget::create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_currency_id' => $data['currency']->id,
|
||||
'amount' => $data['amount'],
|
||||
'start_date' => $data['start'],
|
||||
'end_date' => $data['end'],
|
||||
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AvailableBudget $availableBudget
|
||||
* @param array $data
|
||||
*
|
||||
* @return AvailableBudget
|
||||
*/
|
||||
public function update(AvailableBudget $availableBudget, array $data): AvailableBudget
|
||||
{
|
||||
if (isset($data['amount'])) {
|
||||
$availableBudget->amount = $data['amount'];
|
||||
}
|
||||
$availableBudget->save();
|
||||
|
||||
return $availableBudget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AvailableBudget $availableBudget
|
||||
* @param array $data
|
||||
@@ -204,6 +281,4 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
return $availableBudget;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -39,15 +39,52 @@ interface AvailableBudgetRepositoryInterface
|
||||
*/
|
||||
public function destroyAvailableBudget(AvailableBudget $availableBudget): void;
|
||||
|
||||
/**
|
||||
* Return a list of all available budgets (in all currencies) (for the selected period).
|
||||
*
|
||||
* @param Carbon|null $start
|
||||
* @param Carbon|null $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function get(?Carbon $start = null, ?Carbon $end = null): Collection;
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return string
|
||||
* @deprecated
|
||||
*/
|
||||
public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return AvailableBudget|null
|
||||
*/
|
||||
public function store(array $data): ?AvailableBudget;
|
||||
|
||||
/**
|
||||
* @param AvailableBudget $availableBudget
|
||||
* @param array $data
|
||||
*
|
||||
* @return AvailableBudget
|
||||
*/
|
||||
public function update(AvailableBudget $availableBudget, array $data): AvailableBudget;
|
||||
|
||||
/**
|
||||
* Find existing AB.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return AvailableBudget|null
|
||||
*/
|
||||
public function find(TransactionCurrency $currency, Carbon $start, Carbon $end): ?AvailableBudget;
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
|
@@ -55,6 +55,36 @@ class BudgetLimitRepository implements 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
|
||||
{
|
||||
$query = BudgetLimit
|
||||
::where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $end->format('Y-m-d 00:00:00'))
|
||||
->where('transaction_currency_id', $currency->id);
|
||||
if (null !== $budgets && $budgets->count() > 0) {
|
||||
$query->whereIn('budget_id', $budgets->pluck('id')->toArray());
|
||||
}
|
||||
$set = $query->get(['budget_limits.*']);
|
||||
$result = '0';
|
||||
/** @var BudgetLimit $budgetLimit */
|
||||
foreach ($set as $budgetLimit) {
|
||||
$result = bcadd($budgetLimit->amount, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a budget limit.
|
||||
*
|
||||
@@ -142,7 +172,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
return $set;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
* @param Carbon $start
|
||||
@@ -159,7 +188,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Budget $budget
|
||||
* @param Carbon $start
|
||||
|
@@ -82,6 +82,19 @@ interface BudgetLimitRepositoryInterface
|
||||
*/
|
||||
public function storeBudgetLimit(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 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;
|
||||
|
||||
/**
|
||||
* @param BudgetLimit $budgetLimit
|
||||
* @param array $data
|
||||
|
@@ -28,6 +28,7 @@ use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\RuleAction;
|
||||
use FireflyIII\Models\RuleTrigger;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Services\Internal\Destroy\BudgetDestroyService;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -192,7 +193,7 @@ class BudgetRepository implements BudgetRepositoryInterface
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$set = $this->user->budgets()->where('active', 1)
|
||||
->orderBy('order', 'DESC')
|
||||
->orderBy('order', 'ASC')
|
||||
->orderBy('name', 'ASC')
|
||||
->get();
|
||||
|
||||
|
@@ -29,6 +29,7 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -61,7 +62,6 @@ class OperationsRepository implements OperationsRepositoryInterface
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return string
|
||||
* @deprecated
|
||||
*/
|
||||
public function budgetedPerDay(Budget $budget): string
|
||||
{
|
||||
@@ -278,6 +278,53 @@ class OperationsRepository implements OperationsRepositoryInterface
|
||||
return $return;
|
||||
}
|
||||
|
||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection|null $accounts
|
||||
* @param Collection|null $budgets
|
||||
* @param TransactionCurrency|null $currency
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null
|
||||
): array {
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]);
|
||||
|
||||
if (null !== $accounts && $accounts->count() > 0) {
|
||||
$collector->setAccounts($accounts);
|
||||
}
|
||||
if (null === $budgets || (null !== $budgets && 0 === $budgets->count())) {
|
||||
$budgets = $this->getBudgets();
|
||||
}
|
||||
if (null !== $currency) {
|
||||
$collector->setCurrency($currency);
|
||||
}
|
||||
$collector->setBudgets($budgets);
|
||||
$collector->withBudgetInformation();
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$array = [];
|
||||
|
||||
foreach ($journals as $journal) {
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$array[$currencyId] = $array[$currencyId] ?? [
|
||||
'sum' => '0',
|
||||
'currency_id' => $currencyId,
|
||||
'currency_name' => $journal['currency_name'],
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_decimal_places' => $journal['currency_decimal_places'],
|
||||
];
|
||||
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount']));
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, simply refer to whichever repository holds this function.
|
||||
* TODO might be done better in the future.
|
||||
@@ -295,4 +342,15 @@ class OperationsRepository implements OperationsRepositoryInterface
|
||||
|
||||
return $blRepository->getBudgetLimits($budget, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getBudgets(): Collection
|
||||
{
|
||||
/** @var BudgetRepositoryInterface $repos */
|
||||
$repos = app(BudgetRepositoryInterface::class);
|
||||
|
||||
return $repos->getActiveBudgets();
|
||||
}
|
||||
}
|
@@ -25,6 +25,7 @@ namespace FireflyIII\Repositories\Budget;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -40,10 +41,11 @@ interface OperationsRepositoryInterface
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return string
|
||||
* @deprecated
|
||||
*/
|
||||
public function budgetedPerDay(Budget $budget): string;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This method collects various info on budgets, used on the budget page and on the index.
|
||||
*
|
||||
@@ -96,4 +98,17 @@ interface OperationsRepositoryInterface
|
||||
*/
|
||||
public function spentInPeriodMc(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array;
|
||||
|
||||
/** @noinspection MoreThanThreeArgumentsInspection */
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection|null $accounts
|
||||
* @param Collection|null $budgets
|
||||
* @param TransactionCurrency|null $currency
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null
|
||||
): array;
|
||||
|
||||
}
|
@@ -228,6 +228,7 @@ class OperationsRepository implements OperationsRepositoryInterface
|
||||
$categories = $this->getCategories();
|
||||
}
|
||||
$collector->setCategories($categories);
|
||||
$collector->withCategoryInformation();
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$array = [];
|
||||
|
||||
|
@@ -392,6 +392,16 @@ class CurrencyRepository implements CurrencyRepositoryInterface
|
||||
return TransactionCurrency::orderBy('code', 'ASC')->get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getEnabled(): Collection
|
||||
{
|
||||
return TransactionCurrency::where('enabled',true)->orderBy('code', 'ASC')->get();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
*
|
||||
|
@@ -34,12 +34,6 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
interface CurrencyRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @param string $search
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchCurrency(string $search): Collection;
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
@@ -200,6 +194,11 @@ interface CurrencyRepositoryInterface
|
||||
*/
|
||||
public function getCurrencyByPreference(Preference $preference): TransactionCurrency;
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getEnabled(): Collection;
|
||||
|
||||
/**
|
||||
* Get currency exchange rate.
|
||||
*
|
||||
@@ -220,6 +219,13 @@ interface CurrencyRepositoryInterface
|
||||
*/
|
||||
public function getExchangeRates(TransactionCurrency $currency): Collection;
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchCurrency(string $search): Collection;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
|
@@ -122,9 +122,17 @@ trait DateCalculation
|
||||
$count = 0;
|
||||
|
||||
while ($count < 12) {
|
||||
$format = $current->format('Y-m-d');
|
||||
$loop[$format] = app('navigation')->periodShow($current, $range);
|
||||
$current = app('navigation')->endOfPeriod($current, $range);
|
||||
$currentStart = app('navigation')->startOfPeriod($current, $range);
|
||||
|
||||
$loop[] = [
|
||||
'label' => $current->format('Y-m-d'),
|
||||
'title' => app('navigation')->periodShow($current, $range),
|
||||
'start' => clone $currentStart,
|
||||
'end' => clone $current,
|
||||
];
|
||||
|
||||
|
||||
++$count;
|
||||
$current->addDay();
|
||||
}
|
||||
@@ -151,8 +159,13 @@ trait DateCalculation
|
||||
while ($count < 12) {
|
||||
$current->subDay();
|
||||
$current = app('navigation')->startOfPeriod($current, $range);
|
||||
$format = $current->format('Y-m-d');
|
||||
$loop[$format] = app('navigation')->periodShow($current, $range);
|
||||
$currentEnd = app('navigation')->endOfPeriod($current, $range);
|
||||
$loop[] = [
|
||||
'label' => $current->format('Y-m-d'),
|
||||
'title' => app('navigation')->periodShow($current, $range),
|
||||
'start' => clone $current,
|
||||
'end' => clone $currentEnd,
|
||||
];
|
||||
++$count;
|
||||
}
|
||||
|
||||
|
@@ -143,7 +143,7 @@ class AmountFormat extends Twig_Extension
|
||||
{
|
||||
return new Twig_SimpleFilter(
|
||||
'formatAmountPlain',
|
||||
function (string $string): string {
|
||||
static function (string $string): string {
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
|
||||
return app('amount')->formatAnything($currency, $string, false);
|
||||
|
296
public/v1/js/ff/budgets/index.js
vendored
296
public/v1/js/ff/budgets/index.js
vendored
@@ -18,31 +18,33 @@
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** global: infoIncomeUri, page, token, spent, budgeted, available, currencySymbol, budgetIndexUri, updateIncomeUri, periodStart, periodEnd, budgetAmountUri, accounting */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
$(function () {
|
||||
"use strict";
|
||||
|
||||
$('.updateIncome').on('click', updateIncome);
|
||||
//$('.updateIncome').on('click', updateIncome);
|
||||
|
||||
/*
|
||||
On start, fill the "spent"-bar using the content from the page.
|
||||
*/
|
||||
drawSpentBar();
|
||||
drawBudgetedBar();
|
||||
//drawSpentBar();
|
||||
//drawBudgetedBar();
|
||||
|
||||
$('.update_ab').on('click', updateAvailableBudget)
|
||||
$('.create_ab_alt').on('click', createAltAvailableBudget)
|
||||
|
||||
/*
|
||||
When the input changes, update the percentages for the budgeted bar:
|
||||
*/
|
||||
$('input[type="number"]').on('change', updateBudgetedAmounts);
|
||||
//$('input[type="number"]').on('change', updateBudgetedAmounts);
|
||||
|
||||
//
|
||||
$('.selectPeriod').change(function (e) {
|
||||
var sel = $(e.target).val();
|
||||
if (sel !== "x") {
|
||||
var newUri = budgetIndexUri.replace("REPLACE", sel);
|
||||
var selected = $(e.currentTarget);
|
||||
if (selected.find(":selected").val() !== "x") {
|
||||
var newUri = budgetIndexUri.replace("START", selected.find(":selected").data('start')).replace('END', selected.find(":selected").data('end'));
|
||||
console.log(newUri);
|
||||
window.location.assign(newUri + "?page=" + page);
|
||||
}
|
||||
});
|
||||
@@ -91,7 +93,6 @@ var fixHelper = function (e, tr) {
|
||||
function sortStop(event, ui) {
|
||||
"use strict";
|
||||
|
||||
|
||||
//var current = $(ui.item);
|
||||
var list = $('.sortable-table tbody tr');
|
||||
var submit = [];
|
||||
@@ -109,132 +110,157 @@ function sortStop(event, ui) {
|
||||
};
|
||||
$.post('budgets/reorder', arr);
|
||||
}
|
||||
|
||||
|
||||
function drawSpentBar() {
|
||||
"use strict";
|
||||
if ($('.spentBar').length > 0) {
|
||||
var overspent = spent > budgeted;
|
||||
var pct;
|
||||
|
||||
if (overspent) {
|
||||
// draw overspent bar
|
||||
pct = (budgeted / spent) * 100;
|
||||
$('.spentBar .progress-bar-warning').css('width', pct + '%');
|
||||
$('.spentBar .progress-bar-danger').css('width', (100 - pct) + '%');
|
||||
} else {
|
||||
// draw normal bar:
|
||||
pct = (spent / budgeted) * 100;
|
||||
$('.spentBar .progress-bar-info').css('width', pct + '%');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawBudgetedBar() {
|
||||
"use strict";
|
||||
|
||||
if ($('.budgetedBar').length > 0) {
|
||||
var budgetedMuch = budgeted > available;
|
||||
|
||||
// recalculate percentage:
|
||||
|
||||
var pct;
|
||||
if (budgetedMuch) {
|
||||
// budgeted too much.
|
||||
pct = (available / budgeted) * 100;
|
||||
$('.budgetedBar .progress-bar-warning').css('width', pct + '%');
|
||||
$('.budgetedBar .progress-bar-danger').css('width', (100 - pct) + '%');
|
||||
$('.budgetedBar .progress-bar-info').css('width', 0);
|
||||
} else {
|
||||
pct = (budgeted / available) * 100;
|
||||
$('.budgetedBar .progress-bar-warning').css('width', 0);
|
||||
$('.budgetedBar .progress-bar-danger').css('width', 0);
|
||||
$('.budgetedBar .progress-bar-info').css('width', pct + '%');
|
||||
}
|
||||
|
||||
$('#budgetedAmount').html(currencySymbol + ' ' + budgeted.toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param e
|
||||
*/
|
||||
function updateBudgetedAmounts(e) {
|
||||
"use strict";
|
||||
var target = $(e.target);
|
||||
var id = target.data('id');
|
||||
var leftCell = $('td[class$="left"][data-id="' + id + '"]');
|
||||
var link = $('a[data-id="' + id + '"][class="budget-link"]');
|
||||
var value = target.val();
|
||||
var original = target.data('original');
|
||||
|
||||
// disable input
|
||||
target.prop('disabled', true);
|
||||
|
||||
// replace link (for now)
|
||||
link.attr('href', '#');
|
||||
|
||||
// replace "left" with spinner.
|
||||
leftCell.empty().html('<i class="fa fa-fw fa-spin fa-spinner"></i>');
|
||||
|
||||
// send a post to Firefly to update the amount:
|
||||
var newUri = budgetAmountUri.replace("REPLACE", id);
|
||||
|
||||
$.post(newUri, {amount: value, start: periodStart, end: periodEnd, _token: token}).done(function (data) {
|
||||
|
||||
// difference between new value and original value
|
||||
var difference = value - original;
|
||||
|
||||
// update budgeted value
|
||||
budgeted = budgeted + difference;
|
||||
|
||||
// fill in "left" value:
|
||||
|
||||
|
||||
if (data.left_per_day !== null) {
|
||||
leftCell.html(data.left + ' (' + data.left_per_day + ')');
|
||||
} else {
|
||||
leftCell.html(data.left);
|
||||
}
|
||||
|
||||
// update "budgeted" input:
|
||||
target.val(data.amount);
|
||||
|
||||
// enable thing again
|
||||
target.prop('disabled', false);
|
||||
|
||||
// set new original value:
|
||||
target.data('original', data.amount);
|
||||
|
||||
// run drawBudgetedBar() again:
|
||||
drawBudgetedBar();
|
||||
|
||||
// update the link if relevant:
|
||||
link.attr('href', 'budgets/show/' + id);
|
||||
if (data.limit > 0) {
|
||||
link.attr('href', 'budgets/show/' + id + '/' + data.limit);
|
||||
}
|
||||
|
||||
// update the warning if relevant:
|
||||
if (data.large_diff === true) {
|
||||
$('span[class$="budget_warning"][data-id="' + id + '"]').html(data.warn_text).show();
|
||||
console.log('Show warning for budget');
|
||||
} else {
|
||||
$('span[class$="budget_warning"][data-id="' + id + '"]').empty().hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function updateIncome() {
|
||||
"use strict";
|
||||
$('#defaultModal').empty().load(updateIncomeUri, function () {
|
||||
function createAltAvailableBudget(e) {
|
||||
var button = $(e.currentTarget);
|
||||
$('#defaultModal').empty().load(createAltAvailableBudgetUri, function () {
|
||||
$('#defaultModal').modal('show');
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
function updateAvailableBudget(e) {
|
||||
var button = $(e.currentTarget);
|
||||
var abId = parseInt(button.data('id'));
|
||||
if (0 === abId) {
|
||||
$('#defaultModal').empty().load(createAvailableBudgetUri, function () {
|
||||
$('#defaultModal').modal('show');
|
||||
});
|
||||
}
|
||||
if (abId > 0) {
|
||||
// edit URL.
|
||||
$('#defaultModal').empty().load(editAvailableBudgetUri.replace('REPLACEME', abId), function () {
|
||||
$('#defaultModal').modal('show');
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// function drawSpentBar() {
|
||||
// "use strict";
|
||||
// if ($('.spentBar').length > 0) {
|
||||
// var overspent = spent > budgeted;
|
||||
// var pct;
|
||||
//
|
||||
// if (overspent) {
|
||||
// // draw overspent bar
|
||||
// pct = (budgeted / spent) * 100;
|
||||
// $('.spentBar .progress-bar-warning').css('width', pct + '%');
|
||||
// $('.spentBar .progress-bar-danger').css('width', (100 - pct) + '%');
|
||||
// } else {
|
||||
// // draw normal bar:
|
||||
// pct = (spent / budgeted) * 100;
|
||||
// $('.spentBar .progress-bar-info').css('width', pct + '%');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function drawBudgetedBar() {
|
||||
// "use strict";
|
||||
//
|
||||
// if ($('.budgetedBar').length > 0) {
|
||||
// var budgetedMuch = budgeted > available;
|
||||
//
|
||||
// // recalculate percentage:
|
||||
//
|
||||
// var pct;
|
||||
// if (budgetedMuch) {
|
||||
// // budgeted too much.
|
||||
// pct = (available / budgeted) * 100;
|
||||
// $('.budgetedBar .progress-bar-warning').css('width', pct + '%');
|
||||
// $('.budgetedBar .progress-bar-danger').css('width', (100 - pct) + '%');
|
||||
// $('.budgetedBar .progress-bar-info').css('width', 0);
|
||||
// } else {
|
||||
// pct = (budgeted / available) * 100;
|
||||
// $('.budgetedBar .progress-bar-warning').css('width', 0);
|
||||
// $('.budgetedBar .progress-bar-danger').css('width', 0);
|
||||
// $('.budgetedBar .progress-bar-info').css('width', pct + '%');
|
||||
// }
|
||||
//
|
||||
// $('#budgetedAmount').html(currencySymbol + ' ' + budgeted.toFixed(2));
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// *
|
||||
// * @param e
|
||||
// */
|
||||
// function updateBudgetedAmounts(e) {
|
||||
// "use strict";
|
||||
// var target = $(e.target);
|
||||
// var id = target.data('id');
|
||||
// var leftCell = $('td[class$="left"][data-id="' + id + '"]');
|
||||
// var link = $('a[data-id="' + id + '"][class="budget-link"]');
|
||||
// var value = target.val();
|
||||
// var original = target.data('original');
|
||||
//
|
||||
// // disable input
|
||||
// target.prop('disabled', true);
|
||||
//
|
||||
// // replace link (for now)
|
||||
// link.attr('href', '#');
|
||||
//
|
||||
// // replace "left" with spinner.
|
||||
// leftCell.empty().html('<i class="fa fa-fw fa-spin fa-spinner"></i>');
|
||||
//
|
||||
// // send a post to Firefly to update the amount:
|
||||
// var newUri = budgetAmountUri.replace("REPLACE", id);
|
||||
//
|
||||
// $.post(newUri, {amount: value, start: periodStart, end: periodEnd, _token: token}).done(function (data) {
|
||||
//
|
||||
// // difference between new value and original value
|
||||
// var difference = value - original;
|
||||
//
|
||||
// // update budgeted value
|
||||
// budgeted = budgeted + difference;
|
||||
//
|
||||
// // fill in "left" value:
|
||||
//
|
||||
//
|
||||
// if (data.left_per_day !== null) {
|
||||
// leftCell.html(data.left + ' (' + data.left_per_day + ')');
|
||||
// } else {
|
||||
// leftCell.html(data.left);
|
||||
// }
|
||||
//
|
||||
// // update "budgeted" input:
|
||||
// target.val(data.amount);
|
||||
//
|
||||
// // enable thing again
|
||||
// target.prop('disabled', false);
|
||||
//
|
||||
// // set new original value:
|
||||
// target.data('original', data.amount);
|
||||
//
|
||||
// // run drawBudgetedBar() again:
|
||||
// drawBudgetedBar();
|
||||
//
|
||||
// // update the link if relevant:
|
||||
// link.attr('href', 'budgets/show/' + id);
|
||||
// if (data.limit > 0) {
|
||||
// link.attr('href', 'budgets/show/' + id + '/' + data.limit);
|
||||
// }
|
||||
//
|
||||
// // update the warning if relevant:
|
||||
// if (data.large_diff === true) {
|
||||
// $('span[class$="budget_warning"][data-id="' + id + '"]').html(data.warn_text).show();
|
||||
// console.log('Show warning for budget');
|
||||
// } else {
|
||||
// $('span[class$="budget_warning"][data-id="' + id + '"]').empty().hide();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// /**
|
||||
// *
|
||||
// * @returns {boolean}
|
||||
// */
|
||||
// function updateIncome() {
|
||||
// "use strict";
|
||||
// $('#defaultModal').empty().load(updateIncomeUri, function () {
|
||||
// $('#defaultModal').modal('show');
|
||||
// });
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
@@ -670,14 +670,25 @@ return [
|
||||
'options' => 'Options',
|
||||
|
||||
// budgets:
|
||||
'total_available_budget' => 'Total available budget (between :start and :end)',
|
||||
'total_available_budget_in_currency' => 'Total available budget in :currency',
|
||||
'see_below' => 'see below',
|
||||
'create_new_budget' => 'Create a new budget',
|
||||
'store_new_budget' => 'Store new budget',
|
||||
'stored_new_budget' => 'Stored new budget ":name"',
|
||||
'available_between' => 'Available between :start and :end',
|
||||
'transactionsWithoutBudget' => 'Expenses without budget',
|
||||
'transactions_no_budget' => 'Expenses without budget between :start and :end',
|
||||
'spent_between' => 'Spent between :start and :end',
|
||||
'spent_between' => 'Already spent between :start and :end',
|
||||
'set_available_amount' => 'Set available amount',
|
||||
'update_available_amount' => 'Update available amount',
|
||||
'ab_basic_modal_explain' => 'Use this form to indicate how much you expect to be able to budget (in total, in :currency) in the indicated period.',
|
||||
'createBudget' => 'New budget',
|
||||
'invalid_currency' => 'This is an invalid currency',
|
||||
'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',
|
||||
'alt_currency_ab_create' => 'Set the available budget in another currency',
|
||||
'inactiveBudgets' => 'Inactive budgets',
|
||||
'without_budget_between' => 'Transactions without a budget between :start and :end',
|
||||
'delete_budget' => 'Delete budget ":name"',
|
||||
|
@@ -0,0 +1,38 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span><span class="sr-only">{{ 'close'|_ }}</span>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
{{ trans('firefly.update_budget_amount_range',
|
||||
{start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<form style="display: inline;" id="income" action="{{ route('available-budgets.store') }}" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="start" value="{{ start.format('Y-m-d') }}"/>
|
||||
<input type="hidden" name="end" value="{{ end.format('Y-m-d') }}"/>
|
||||
<input type="hidden" name="page" value="{{ page }}"/>
|
||||
<input type="hidden" name="currency_id" value="{{ currency.id }}"/>
|
||||
|
||||
<div class="form-group">
|
||||
<select class="form-control" name="currency_id">
|
||||
{% for currency in currencies %}
|
||||
<option label="{{ currency.name }}" value="{{ currency.id }}">{{ currency.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input step="any" class="form-control" id="amount" value="" autocomplete="off" name="amount" type="number"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ 'set_available_amount'|_ }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
37
resources/views/v1/budgets/available-budgets/create.twig
Normal file
37
resources/views/v1/budgets/available-budgets/create.twig
Normal file
@@ -0,0 +1,37 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span><span class="sr-only">{{ 'close'|_ }}</span>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
{{ trans('firefly.update_budget_amount_range',
|
||||
{start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<form style="display: inline;" id="income" action="{{ route('available-budgets.store') }}" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="start" value="{{ start.format('Y-m-d') }}"/>
|
||||
<input type="hidden" name="end" value="{{ end.format('Y-m-d') }}"/>
|
||||
<input type="hidden" name="page" value="{{ page }}"/>
|
||||
<input type="hidden" name="currency_id" value="{{ currency.id }}"/>
|
||||
|
||||
<div class="form-group">
|
||||
<p class="form-control-static">{{ trans('firefly.ab_basic_modal_explain', {currency: currency.name}) }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">{{ currency.symbol|raw }}</div>
|
||||
<input step="any" class="form-control" id="amount" value="" autocomplete="off" name="amount" type="number"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ 'set_available_amount'|_ }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
33
resources/views/v1/budgets/available-budgets/edit.twig
Normal file
33
resources/views/v1/budgets/available-budgets/edit.twig
Normal file
@@ -0,0 +1,33 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span><span class="sr-only">{{ 'close'|_ }}</span>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
{{ trans('firefly.update_budget_amount_range',
|
||||
{start: availableBudget.start_date.formatLocalized(monthAndDayFormat), end: availableBudget.end_date.formatLocalized(monthAndDayFormat)}) }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<form style="display: inline;" id="income" action="{{ route('available-budgets.update', [availableBudget.id]) }}" method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="page" value="{{ page }}"/>
|
||||
<div class="form-group">
|
||||
<p class="form-control-static">{{ trans('firefly.ab_basic_modal_explain', {currency: availableBudget.transactionCurrency.name}) }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">{{ availableBudget.transactionCurrency.symbol|raw }}</div>
|
||||
<input step="any" class="form-control" id="amount" value="{{ availableBudget.amount }}" autocomplete="off" name="amount" type="number"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ 'update_available_amount'|_ }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -5,21 +5,206 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- date selector -->
|
||||
<div class="row">
|
||||
<div class="col-lg-9 col-md-8 col-sm-12 col-xs-12">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ start.formatLocalized(monthAndDayFormat) }} — {{ end.formatLocalized(monthAndDayFormat) }}</h3>
|
||||
<h3 class="box-title">{{ 'budget_period_navigator'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row" id="periodNavigator">
|
||||
<div class="col-lg-2 col-md-4 col-sm-12 col-xs-12">
|
||||
<select class="form-control selectPeriod" name="previous">
|
||||
<option label="{{ 'select_date'|_ }}" value="x">{{ 'select_date'|_ }}</option>
|
||||
{% for array in prevLoop %}
|
||||
<option label="{{ array.title }}" value="{{ array.label }}" data-start="{{ array.start.format('Y-m-d') }}"
|
||||
data-end="{{ array.end.format('Y-m-d') }}">{{ array.title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-lg-8 col-md-4 col-sm-12 col-xs-12 text-center">
|
||||
<div class="btn btn-group btn-group-lg" style="padding-top:0;">
|
||||
<a href="{{ route('budgets.index', [prevLoop[0].start.format('Y-m-d'), prevLoop[0].end.format('Y-m-d')]) }}?page={{ page }}"
|
||||
class="btn btn-default" title="{{ prevLoop[0].title }}">←</a>
|
||||
<a title="{{ start.formatLocalized(monthAndDayFormat) }} - {{ end.formatLocalized(monthAndDayFormat) }}"
|
||||
href="{{ route('budgets.index', [start.format('Y-m-d'), end.format('Y-m-d')]) }}?page={{ page }}"
|
||||
class="btn btn-default">{{ periodTitle }}</a>
|
||||
<a href="{{ route('budgets.index', [nextLoop[0].start.format('Y-m-d'), nextLoop[0].end.format('Y-m-d')]) }}?page={{ page }}"
|
||||
class="btn btn-default" title="{{ nextLoop[0].title }}">→</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-md-4 col-sm-12 col-xs-12 text-right">
|
||||
<select class="form-control selectPeriod" name="next">
|
||||
<option label="{{ 'select_date'|_ }}" value="x">{{ 'select_date'|_ }}</option>
|
||||
{% for array in nextLoop %}
|
||||
<option label="{{ array.title }}" value="{{ array.label }}" data-start="{{ array.start.format('Y-m-d') }}"
|
||||
data-end="{{ array.end.format('Y-m-d') }}">{{ array.title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- available budget configurator. -->
|
||||
{% if availableBudgets|length == 0 %}
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
{{ trans('firefly.total_available_budget_in_currency', {currency: defaultCurrency.name}) }}
|
||||
<br>
|
||||
<small>{{ trans('firefly.between_dates_breadcrumb', {start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}</small>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
{# info about the amount budgeted #}
|
||||
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
|
||||
<small>{{ 'budgeted'|_ }}: <span id="budgetedAmount" class="text-success">{{ budgeted|formatAmountPlain }}</span></small>
|
||||
<small>{{ 'budgeted'|_ }} ({{ 'see_below'|_ }}):
|
||||
<span class="budgeted_amount" data-value="{{ budgeted }}" data-id="0">
|
||||
{{ formatAmountBySymbol(budgeted, defaultCurrency.symbol, defaultCurrency.decimal_places) }}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
{# info about the amount spent #}
|
||||
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9" style="text-align:right;margin-bottom:3px;">
|
||||
<small class="available_bar"
|
||||
data-id="0">{{ trans('firefly.available_between', {start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat) }) }}
|
||||
:
|
||||
<span class="available_amount" data-id="0" data-value="0"
|
||||
data-value="0">{{ formatAmountBySymbol(0, defaultCurrency.symbol, defaultCurrency.decimal_places, true) }}</span>
|
||||
<a href="#" data-id="0" class="update_ab btn btn-default btn-xs"><i class="fa fa-pencil"></i></a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
{# info text to show how much is spent (in currency). #}
|
||||
<div class="row spentInfo" data-id="0" data-value="{{ spent }}">
|
||||
<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)}) }}
|
||||
: {{ formatAmountBySymbol(spent, defaultCurrency.symbol, defaultCurrency.decimal_places) }} </small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if enableAddButton %}
|
||||
<div class="col-lg-12">
|
||||
<p class="pull-right">
|
||||
<a href="#" class="btn btn-success btn-xs create_ab_alt">
|
||||
<i class="fa fa-plus-circle"></i>
|
||||
{{ 'alt_currency_ab_create'|_ }}</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% set boxSize = 12 %}
|
||||
{% if availableBudgets|length > 1 %}
|
||||
{% set boxSize = 6 %}
|
||||
{% endif %}
|
||||
{% if availableBudgets|length > 0 %}
|
||||
<div class="row">
|
||||
{% for budget in availableBudgets %}
|
||||
<div class="col-lg-{{ boxSize }} col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
{{ trans('firefly.total_available_budget_in_currency', {currency: budget.transaction_currency.name}) }}
|
||||
<br>
|
||||
<small>{{ trans('firefly.between_dates_breadcrumb', {start: budget.start_date.formatLocalized(monthAndDayFormat), end: budget.end_date.formatLocalized(monthAndDayFormat)}) }}</small>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
{# info about the amount budgeted #}
|
||||
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
|
||||
<small>{{ 'budgeted'|_ }}:
|
||||
<span class="text-success budgeted_amount" data-id="{{ budget.id }}">
|
||||
{{ formatAmountBySymbol(budget.budgeted, budget.transaction_currency.symbol, budget.transaction_currency.decimal_places, false) }}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
{# info about the amount spent #}
|
||||
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9" style="text-align:right;margin-bottom:3px;">
|
||||
<small class="available_bar"
|
||||
data-id="{{ budget.id }}">{{ trans('firefly.available_between', {start: budget.start_date.formatLocalized(monthAndDayFormat), end: budget.end_date.formatLocalized(monthAndDayFormat) }) }}
|
||||
:
|
||||
<span class="available_amount" data-id="{{ budget.id }}"
|
||||
data-value="{{ budget.amount }}">{{ formatAmountBySymbol(budget.amount, budget.transaction_currency.symbol, budget.transaction_currency.decimal_places, true) }}</span>
|
||||
<a href="#" data-id="{{ budget.id }}" class="update_ab btn btn-default btn-xs"><i class="fa fa-pencil"></i></a>
|
||||
<a href="{{ route('available-budgets.delete', [budget.id]) }}" data-id="{{ budget.id }}"
|
||||
class="delete_ab btn btn-danger btn-xs"><i class="fa fa-trash"></i></a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
{# 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-bar progress-bar-danger" data-id="{{ budget.id }}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-warning" data-id="{{ budget.id }}" role="progressbar" aria-valuenow="10"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
<div class="progress-bar progress-bar-info" data-id="{{ budget.id }}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# info text to show how much is spent (in currency). #}
|
||||
<div class="row spentInfo" data-id="{{ budget.id }}">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<small>{{ trans('firefly.spent_between', {start: budget.start_date.formatLocalized(monthAndDayFormat), end: budget.start_date.formatLocalized(monthAndDayFormat)}) }}
|
||||
: {{ formatAmountBySymbol(budget.spent, budget.transaction_currency.symbol, budget.transaction_currency.decimal_places) }} </small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# 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-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>
|
||||
{% endfor %}
|
||||
{% if enableAddButton %}
|
||||
<div class="col-lg-12">
|
||||
<p class="pull-right">
|
||||
<a href="#" class="btn btn-success btn-xs create_ab_alt">
|
||||
<i class="fa fa-plus-circle"></i>
|
||||
{{ 'alt_currency_ab_create'|_ }}</a>
|
||||
</p>
|
||||
</div>
|
||||
{% 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 }}">{{ available|formatAmountPlain }}</span>
|
||||
<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>
|
||||
@@ -40,7 +225,7 @@
|
||||
<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)}) }}
|
||||
: {{ spent|formatAmount }}</small>
|
||||
: {{ "-1"|formatAmount }}{{ spent|formatAmount }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -55,78 +240,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-md-4 col-sm-12 col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'transactionsWithoutBudget'|_ }}</h3>
|
||||
</div>
|
||||
<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: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% 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>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<a href="{{ route('budgets.create') }}" class="btn btn-success pull-right">{{ 'createBudget'|_ }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>#}
|
||||
{% if paginator.count == 0 and inactive.count == 0 and page == 1 %}
|
||||
{% include 'partials.empty' with {objectType: 'default', type: 'budgets',route: route('budgets.create')} %}
|
||||
{# make FF ignore demo for now. #}
|
||||
{% set shownDemo = true %}
|
||||
{% endif %}
|
||||
|
||||
{# date thing #}
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'budget_period_navigator'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row" id="periodNavigator">
|
||||
<div class="col-lg-2 col-md-4 col-sm-12 col-xs-12">
|
||||
<select class="form-control selectPeriod" name="previous">
|
||||
<option label="{{ 'select_date'|_ }}" value="x">{{ 'select_date'|_ }}</option>
|
||||
{% for format, previousLabel in previousLoop %}
|
||||
<option label="{{ previousLabel }}" value="{{ format }}">{{ previousLabel }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-lg-8 col-md-4 col-sm-12 col-xs-12 text-center">
|
||||
<div class="btn btn-group btn-group-lg" style="padding-top:0;">
|
||||
<a href="{{ route('budgets.index', [prev.format('Y-m-d')]) }}?page={{ page }}" class="btn btn-default" title="{{ prevText }}">←</a>
|
||||
<a href="{{ route('budgets.index', [start.format('Y-m-d')]) }}?page={{ page }}" class="btn btn-default">{{ currentMonth }}</a>
|
||||
<a href="{{ route('budgets.index', [next.format('Y-m-d')]) }}?page={{ page }}" class="btn btn-default" title="{{ nextText }}">→</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-md-4 col-sm-12 col-xs-12 text-right">
|
||||
<select class="form-control selectPeriod" name="next">
|
||||
<option label="{{ 'select_date'|_ }}" value="x">{{ 'select_date'|_ }}</option>
|
||||
{% for format, nextLabel in nextLoop %}
|
||||
<option label="{{ nextLabel }}" value="{{ format }}">{{ nextLabel }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
@@ -162,6 +286,7 @@
|
||||
</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"
|
||||
@@ -188,14 +313,18 @@
|
||||
</td>
|
||||
<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'] / activeDaysPassed)|formatAmount }})
|
||||
{{ "-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']) }}">
|
||||
{{ (repAmount + budgetInformation[budget.id]['spent'])|formatAmount }}
|
||||
{{ "-1"|formatAmount }}
|
||||
{#{{ (repAmount + budgetInformation[budget.id]['spent'])|formatAmount }}#}
|
||||
{% if repAmount + budgetInformation[budget.id]['spent'] > 0 %}
|
||||
({{ ((repAmount + budgetInformation[budget.id]['spent']) / activeDaysLeft)|formatAmount }})
|
||||
({{ "-1"|formatAmount }})
|
||||
{#({{ ((repAmount + budgetInformation[budget.id]['spent']) / activeDaysLeft)|formatAmount }})#}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -213,8 +342,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if inactive|length > 0 %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-md-4 col-sm-12 col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'transactionsWithoutBudget'|_ }}</h3>
|
||||
</div>
|
||||
<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: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if paginator.count > 0 and inactive.count > 0 %}
|
||||
<div class="col-lg-3 col-md-4 col-sm-12 col-xs-12">
|
||||
<div class="box" id="createBudgetBox">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'createBudget'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<a href="{{ route('budgets.create') }}" class="btn btn-success pull-right">{{ 'createBudget'|_ }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if inactive|length > 0 %}
|
||||
<div class="col-lg-3 col-sm-4 col-md-6">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
@@ -231,8 +387,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
@@ -243,14 +400,17 @@
|
||||
<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 = {{ spent * -1 }}; // must be positive for the calculation to work.
|
||||
var spent = 0;
|
||||
var page = {{ page }};
|
||||
// budgeted data:
|
||||
var budgeted = {{ budgeted }};
|
||||
var available = {{ available }};
|
||||
var budgetIndexUri = "{{ route('budgets.index','REPLACE') }}";
|
||||
var budgetAmountUri = "{{ route('budgets.amount','REPLACE') }}";
|
||||
var updateIncomeUri = "{{ route('budgets.income',[start.format('Y-m-d'),end.format('Y-m-d')]) }}?page={{ page }}";
|
||||
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 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') }}";
|
||||
var periodEnd = "{{ end.format('Y-m-d') }}";
|
||||
</script>
|
||||
|
@@ -220,11 +220,41 @@ Route::group(
|
||||
Route::get('{start_date?}/{end_date?}', ['uses' => 'Budget\IndexController@index', 'as' => 'index']);
|
||||
|
||||
// update budget amount and income amount
|
||||
Route::get('income/{start_date}/{end_date}', ['uses' => 'Budget\AmountController@updateIncome', 'as' => 'income']);
|
||||
Route::post('income', ['uses' => 'Budget\AmountController@postUpdateIncome', 'as' => 'income.post']);
|
||||
Route::post('amount/{budget}', ['uses' => 'Budget\AmountController@amount', 'as' => 'amount']);
|
||||
//Route::get('income/{start_date}/{end_date}', ['uses' => 'Budget\AmountController@updateIncome', 'as' => 'income']);
|
||||
//Route::post('income', ['uses' => 'Budget\AmountController@postUpdateIncome', 'as' => 'income.post']);
|
||||
//Route::post('amount/{budget}', ['uses' => 'Budget\AmountController@amount', 'as' => 'amount']);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Available Budget Controller
|
||||
*/
|
||||
Route::group(
|
||||
['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']);
|
||||
//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']);
|
||||
|
||||
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']);
|
||||
}
|
||||
);
|
||||
|
||||
|
Reference in New Issue
Block a user