diff --git a/app/Http/Controllers/Budget/AvailableBudgetController.php b/app/Http/Controllers/Budget/AvailableBudgetController.php deleted file mode 100644 index 55e5bfbc19..0000000000 --- a/app/Http/Controllers/Budget/AvailableBudgetController.php +++ /dev/null @@ -1,267 +0,0 @@ -. - */ - -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\Currency\CurrencyRepositoryInterface; -use Illuminate\Contracts\View\Factory; -use Illuminate\Http\RedirectResponse; -use Illuminate\Http\Request; -use Illuminate\Routing\Redirector; -use Illuminate\View\View; -use Log; -use ValueError; - -/** - * - * Class AvailableBudgetController - */ -class AvailableBudgetController extends Controller -{ - - /** @var AvailableBudgetRepositoryInterface */ - private $abRepository; - /** @var CurrencyRepositoryInterface */ - private $currencyRepos; - - /** - * 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-pie-chart'); - $this->abRepository = app(AvailableBudgetRepositoryInterface::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. - * - * @param Request $request - * @param Carbon $start - * @param Carbon $end - * @param TransactionCurrency|null $currency - * - * @return Factory|RedirectResponse|Redirector|View - */ - 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. - * - * @param Request $request - * @param Carbon $start - * @param Carbon $end - * - * @return Factory|View - */ - public function createAlternative(Request $request, Carbon $start, Carbon $end) - { - $currencies = $this->currencyRepos->get(); - $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 Request $request - * - * @return RedirectResponse|Redirector - */ - public function delete(Request $request) - { - $id = (int) $request->get('id'); - if (0 !== $id) { - $availableBudget = $this->abRepository->findById($id); - if (null !== $availableBudget) { - $this->abRepository->destroyAvailableBudget($availableBudget); - session()->flash('success', trans('firefly.deleted_ab')); - } - } - - return redirect(route('budgets.index')); - } - - /** - * @param AvailableBudget $availableBudget - * - * @param Carbon $start - * @param Carbon $end - * - * @return Factory|View - */ - public function edit(AvailableBudget $availableBudget, Carbon $start, Carbon $end) - { - $availableBudget->amount = number_format((float) $availableBudget->amount, $availableBudget->transactionCurrency->decimal_places, '.', ''); - - return view('budgets.available-budgets.edit', compact('availableBudget', 'start', 'end')); - } - - /** - * @param Request $request - * - * @return RedirectResponse|Redirector - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface - */ - 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()); - } - - // validate amount - $amount = (string) $request->get('amount'); - if ('' === $amount) { - session()->flash('error', trans('firefly.invalid_amount')); - - return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } - if (bccomp($amount, '0') <= 0) { - session()->flash('error', trans('firefly.invalid_amount')); - - return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } - - // 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', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } - $start->startOfDay(); - $end->endOfDay(); - // find existing AB - $existing = $this->abRepository->find($currency, $start, $end); - if (null === $existing) { - $this->abRepository->store( - [ - 'amount' => $amount, - 'currency_id' => $currency->id, - 'start' => $start, - 'end' => $end, - ] - ); - } - if (null !== $existing) { - // update amount: - $this->abRepository->update($existing, ['amount' => $amount]); - } - session()->flash('success', trans('firefly.set_ab')); - - return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } - - /** - * @param Request $request - * @param AvailableBudget $availableBudget - * - * @param Carbon $start - * @param Carbon $end - * - * @return RedirectResponse|Redirector - */ - public function update(Request $request, AvailableBudget $availableBudget, Carbon $start, Carbon $end) - { - // validate amount - $amount = (string) $request->get('amount'); - if ('' === $amount) { - session()->flash('error', trans('firefly.invalid_amount')); - - return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } - try { - if (bccomp($amount, '0') <= 0) { - session()->flash('error', trans('firefly.invalid_amount')); - - return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } - } catch (ValueError $e) { - Log::error(sprintf('Value "%s" is not a number: %s', $amount, $e->getMessage())); - session()->flash('error', trans('firefly.invalid_amount')); - - return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } - $this->abRepository->update($availableBudget, ['amount' => $amount]); - session()->flash('success', trans('firefly.updated_ab')); - - return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')])); - } -} diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index 6986b402d7..1148858c02 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -129,9 +129,6 @@ class IndexController extends Controller unset($spentArr); } - // count the number of enabled currencies. This determines if we display a "+" button. - $enableAddButton = $currencies->count() > count($availableBudgets); - // number of days for consistent budgeting. $activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description. $activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description. @@ -141,7 +138,7 @@ class IndexController extends Controller return view( 'budgets.index', compact( - 'availableBudgets', 'budgeted', 'spent', 'prevLoop', 'nextLoop', 'budgets', 'currencies', 'enableAddButton', 'periodTitle', + 'availableBudgets', 'budgeted', 'spent', 'prevLoop', 'nextLoop', 'budgets', 'currencies', 'periodTitle', 'defaultCurrency', 'activeDaysPassed', 'activeDaysLeft', 'inactive', 'budgets', 'start', 'end', 'sums' ) ); diff --git a/app/Http/Controllers/Json/BudgetController.php b/app/Http/Controllers/Json/BudgetController.php index af1ff647a1..3d54ebd572 100644 --- a/app/Http/Controllers/Json/BudgetController.php +++ b/app/Http/Controllers/Json/BudgetController.php @@ -102,8 +102,8 @@ class BudgetController extends Controller [ 'budgeted' => $budgeted, 'budgeted_formatted' => app('amount')->formatAnything($currency, $budgeted, true), - 'available' => app('amount')->formatAnything($currency, $available, true), - 'available_formatted' => $available, + 'available' => $available, + 'available_formatted' => app('amount')->formatAnything($currency, $available, true), 'percentage' => $percentage, 'currency_id' => $currency->id, 'currency_code' => $currency->code, diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index dfc3e2fc9e..ea99f2602d 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -39,8 +39,11 @@ use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Events\UserChangedEmail; use FireflyIII\Events\WarnUserAboutBill; use FireflyIII\Mail\OAuthTokenCreatedMail; +use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankRepetition; +use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Auth\Events\Login; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -150,6 +153,54 @@ class EventServiceProvider extends ServiceProvider { parent::boot(); $this->registerCreateEvents(); + $this->registerBudgetEvents(); + } + + /** + * + */ + protected function registerBudgetEvents(): void + { + $func = static function (BudgetLimit $limit) { + Log::debug('Trigger budget limit event.'); + // find available budget with same period and same currency or create it. + // then set it or add money: + $user = $limit->budget->user; + $availableBudget = $user + ->availableBudgets() + ->where('start_date', $limit->start_date->format('Y-m-d')) + ->where('end_date', $limit->end_date->format('Y-m-d')) + ->where('transaction_currency_id', $limit->transaction_currency_id) + ->first(); + // update! + if (null !== $availableBudget) { + + $repository = app(BudgetLimitRepositoryInterface::class); + $repository->setUser($user); + $set = $repository->getAllBudgetLimitsByCurrency($limit->transactionCurrency, $limit->start_date, $limit->end_date); + $sum = (string) $set->sum('amount'); + + + Log::debug(sprintf('Because budget limit #%d had its amount changed to %s, available budget limit #%d will be updated.', $limit->id, $limit->amount, $availableBudget->id)); + $availableBudget->amount = $sum; + $availableBudget->save(); + return; + } + Log::debug('Does not exist, create it.'); + // create it. + $data = [ + 'amount' => $limit->amount, + 'start' => $limit->start_date, + 'end' => $limit->end_date, + 'currency_id' => $limit->transaction_currency_id, + ]; + $repository = app(AvailableBudgetRepositoryInterface::class); + $repository->setUser($user); + $repository->store($data); + }; + + BudgetLimit::created($func); + BudgetLimit::updated($func); } /** @@ -157,6 +208,8 @@ class EventServiceProvider extends ServiceProvider */ protected function registerCreateEvents(): void { + + // in case of repeated piggy banks and/or other problems. PiggyBank::created( static function (PiggyBank $piggyBank) { diff --git a/public/v1/js/ff/budgets/index.js b/public/v1/js/ff/budgets/index.js index 1e48156aa6..a68fa2c9cb 100644 --- a/public/v1/js/ff/budgets/index.js +++ b/public/v1/js/ff/budgets/index.js @@ -29,9 +29,9 @@ $(function () { drawSpentBars(); drawBudgetedBars(); - $('.update_ab').on('click', updateAvailableBudget); - $('.delete_ab').on('click', deleteAvailableBudget); - $('.create_ab_alt').on('click', createAltAvailableBudget); + //$('.update_ab').on('click', updateAvailableBudget); + //$('.delete_ab').on('click', deleteAvailableBudget); + //$('.create_ab_alt').on('click', createAltAvailableBudget); $('.budget_amount').on('change', updateBudgetedAmount); $('.create_bl').on('click', createBudgetLimit); @@ -127,20 +127,28 @@ function updateBudgetedAmount(e) { } function updateTotalBudgetedAmount(currencyId) { + console.log('updateTotalBudgetedAmount'); // fade info away: $('span.budgeted_amount[data-currency="' + currencyId + '"]') .fadeTo(100, 0.1, function () { - //$(this).fadeTo(500, 1.0); + }); + $('span.available_amount[data-currency="' + currencyId + '"]') + .fadeTo(100, 0.1, function () { }); // get new amount: $.get(totalBudgetedUrl.replace('REPLACEME', currencyId)).done(function (data) { // set thing: + $('span.budgeted_amount[data-currency="' + currencyId + '"]') .html(data.budgeted_formatted) // fade back: .fadeTo(300, 1.0); + // also set available amount: + $('span.available_amount[data-currency="' + currencyId + '"]') + .html(data.available_formatted).fadeTo(300, 1.0); + // set bar: var pct = parseFloat(data.percentage); if (pct <= 100) { @@ -214,7 +222,7 @@ function deleteBudgetLimit(e) { var url = deleteBudgetLimitUrl.replace('REPLACEME', budgetLimitId.toString()); $.post(url, {_token: token}).then(function () { $('.bl_entry[data-budget-limit-id="' + budgetLimitId + '"]').remove(); - + }); return false; } diff --git a/resources/views/budgets/available-budgets/create-alternative.twig b/resources/views/budgets/available-budgets/create-alternative.twig deleted file mode 100644 index 126dbb4713..0000000000 --- a/resources/views/budgets/available-budgets/create-alternative.twig +++ /dev/null @@ -1,37 +0,0 @@ - - diff --git a/resources/views/budgets/available-budgets/create.twig b/resources/views/budgets/available-budgets/create.twig deleted file mode 100644 index a26a0933e5..0000000000 --- a/resources/views/budgets/available-budgets/create.twig +++ /dev/null @@ -1,37 +0,0 @@ - - diff --git a/resources/views/budgets/available-budgets/edit.twig b/resources/views/budgets/available-budgets/edit.twig deleted file mode 100644 index aeec8fdc1d..0000000000 --- a/resources/views/budgets/available-budgets/edit.twig +++ /dev/null @@ -1,33 +0,0 @@ - - diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig index 3394388f82..938f01c9f8 100644 --- a/resources/views/budgets/index.twig +++ b/resources/views/budgets/index.twig @@ -77,7 +77,6 @@ : {{ formatAmountBySymbol(0, defaultCurrency.symbol, defaultCurrency.decimal_places, true) }} - @@ -90,15 +89,6 @@ - {% if enableAddButton %} -
-

- - - {{ 'alt_currency_ab_create'|_ }} -

-
- {% endif %} {% endif %} @@ -134,10 +124,8 @@ {{ trans('firefly.available_between', {start: budget.start_date.isoFormat(monthAndDayFormat), end: budget.end_date.isoFormat(monthAndDayFormat) }) }} : - {{ formatAmountBySymbol(budget.amount, budget.transaction_currency.symbol, budget.transaction_currency.decimal_places, true) }} - - @@ -190,15 +178,6 @@ {% endfor %} - {% if enableAddButton %} -
-

- - - {{ 'alt_currency_ab_create'|_ }} -

-
- {% endif %} {% endif %} {% if budgets|length == 0 and inactive.count() == 0 %} @@ -206,7 +185,7 @@ {# make FF ignore demo for now. #} {% set shownDemo = true %} {% else %} - +
@@ -460,12 +439,6 @@ // index route. var budgetIndexUrl = "{{ route('budgets.index',['START','END']) }}"; - // create available budgets / edit - var createAvailableBudgetUrl = "{{ route('available-budgets.create', [start.format('Y-m-d'), end.format('Y-m-d')]) }}"; - var createAltAvailableBudgetUrl = "{{ route('available-budgets.create-alternative', [start.format('Y-m-d'), end.format('Y-m-d')]) }}"; - var editAvailableBudgetUrl = "{{ route('available-budgets.edit', ['REPLACEME', start.format('Y-m-d'), end.format('Y-m-d')]) }}"; - var deleteABUrl = "{{ route('available-budgets.delete') }}"; - // budget limit create form. var createBudgetLimitUrl = "{{ route('budget-limits.create', ['REPLACEME', start.format('Y-m-d'), end.format('Y-m-d')]) }}"; var storeBudgetLimitUrl = "{{ route('budget-limits.store') }}"; diff --git a/routes/web.php b/routes/web.php index bf6376d10f..c90fb57ee9 100644 --- a/routes/web.php +++ b/routes/web.php @@ -260,29 +260,6 @@ Route::group( } ); -/** - * Available Budget Controller. - */ -Route::group( - ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers', 'prefix' => 'available-budgets', 'as' => 'available-budgets.'], - static function () { - - // 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']); - - // edit - Route::get('edit/{availableBudget}/{start_date}/{end_date}', ['uses' => 'Budget\AvailableBudgetController@edit', 'as' => 'edit']); - Route::post('update/{availableBudget}/{start_date}/{end_date}', ['uses' => 'Budget\AvailableBudgetController@update', 'as' => 'update']); - - Route::post('delete', ['uses' => 'Budget\AvailableBudgetController@delete', 'as' => 'delete']); - } -); - /** * Budget Limit Controller. */