Refactor budget controller.

This commit is contained in:
James Cole 2018-07-14 15:22:21 +02:00
parent 6bc772d640
commit b8699422c8
19 changed files with 2087 additions and 1495 deletions

View File

@ -95,8 +95,6 @@ class BudgetLimitController extends Controller
{
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$start = null;
$end = null;
$budgetId = (int)($request->get('budget_id') ?? 0);
$budget = $this->repository->findNull($budgetId);
$this->parameters->set('budget_id', $budgetId);
@ -119,9 +117,11 @@ class BudgetLimitController extends Controller
$collection = new Collection;
if (null === $budget) {
/** @noinspection PhpUndefinedVariableInspection */
$collection = $this->repository->getAllBudgetLimits($start, $end);
}
if (null !== $budget) {
/** @noinspection PhpUndefinedVariableInspection */
$collection = $this->repository->getBudgetLimits($budget, $start, $end);
}

View File

@ -40,8 +40,9 @@ use Preferences;
use View;
/**
*
* Class ShowController
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ShowController extends Controller
{

View File

@ -43,6 +43,8 @@ use View;
/**
* Class BillController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class BillController extends Controller
{
@ -298,6 +300,9 @@ class BillController extends Controller
* @param BillFormRequest $request
*
* @return RedirectResponse
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function store(BillFormRequest $request): RedirectResponse
{
@ -316,7 +321,6 @@ class BillController extends Controller
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
$this->attachments->saveAttachmentsForModel($bill, $files);
// flash messages
if (\count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
}

View File

@ -0,0 +1,276 @@
<?php
/**
* AmountController.php
* Copyright (c) 2018 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 FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\BudgetIncomeRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Log;
use Preferences;
use View;
/**
*
* Class AmountController
*/
class AmountController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Request $request
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
*
* @return JsonResponse
*/
public function amount(Request $request, BudgetRepositoryInterface $repository, Budget $budget): JsonResponse
{
$amount = (string)$request->get('amount');
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
$largeDiff = false;
$warnText = '';
$days = 0;
$daysInMonth = 0;
if (0 === bccomp($amount, '0')) {
$budgetLimit = null;
}
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
Log::debug(sprintf('Start is %s, end is %s, today is %s', $start->format('Y-m-d'), $end->format('Y-m-d'), $today->format('Y-m-d')));
if ($today->gte($start) && $today->lte($end)) {
$days = $end->diffInDays($today);
$daysInMonth = $start->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$days = $start->diffInDays($end);
$daysInMonth = $start->diffInDays($end);
}
$days = 0 === $days ? 1 : $days;
$daysInMonth = 0 === $daysInMonth ? 1 : $daysInMonth;
// calculate left in budget:
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
$currency = app('amount')->getDefaultCurrency();
$left = app('amount')->formatAnything($currency, bcadd($amount, $spent), true);
$leftPerDay = 'none';
// is user has money left, calculate.
if (1 === bccomp(bcadd($amount, $spent), '0')) {
$leftPerDay = app('amount')->formatAnything($currency, bcdiv(bcadd($amount, $spent), (string)$days), true);
}
// over or under budgeting, compared to previous budgets?
$average = $this->repository->budgetedPerDay($budget);
// current average per day:
$diff = $start->diffInDays($end);
$current = $amount;
if ($diff > 0) {
$current = bcdiv($amount, (string)$diff);
}
if (bccomp(bcmul('1.1', $average), $current) === -1) {
$largeDiff = true;
$warnText = (string)trans(
'firefly.over_budget_warn',
[
'amount' => app('amount')->formatAnything($currency, $average, false),
'over_amount' => app('amount')->formatAnything($currency, $current, false),
]
);
}
app('preferences')->mark();
return response()->json(
[
'left' => $left,
'name' => $budget->name,
'limit' => $budgetLimit ? $budgetLimit->id : 0,
'amount' => $amount,
'current' => $current,
'average' => $average,
'large_diff' => $largeDiff,
'left_per_day' => $leftPerDay,
'warn_text' => $warnText,
'daysInMonth' => $daysInMonth,
]
);
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function infoIncome(Carbon $start, Carbon $end)
{
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('info-income');
Log::debug(sprintf('infoIncome start is %s', $start->format('Y-m-d')));
Log::debug(sprintf('infoIncome end is %s', $end->format('Y-m-d')));
if ($cache->has()) {
// @codeCoverageIgnoreStart
$result = $cache->get();
return view('budgets.info', compact('result'));
// @codeCoverageIgnoreEnd
}
$result = [
'available' => '0',
'earned' => '0',
'suggested' => '0',
];
$currency = app('amount')->getDefaultCurrency();
$range = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $begin */
$begin = app('navigation')->subtractPeriod($start, $range, 3);
Log::debug(sprintf('Range is %s', $range));
Log::debug(sprintf('infoIncome begin is %s', $begin->format('Y-m-d')));
// get average amount available.
$total = '0';
$count = 0;
$currentStart = clone $begin;
while ($currentStart < $start) {
Log::debug(sprintf('Loop: currentStart is %s', $currentStart->format('Y-m-d')));
$currentEnd = app('navigation')->endOfPeriod($currentStart, $range);
$total = bcadd($total, $this->repository->getAvailableBudget($currency, $currentStart, $currentEnd));
$currentStart = app('navigation')->addPeriod($currentStart, $range, 0);
++$count;
}
Log::debug('Loop end');
if (0 === $count) {
$count = 1;
}
$result['available'] = bcdiv($total, (string)$count);
// amount earned in this period:
$subDay = clone $end;
$subDay->subDay();
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($begin, $subDay)->setTypes([TransactionType::DEPOSIT])->withOpposingAccount();
$result['earned'] = bcdiv((string)$collector->getJournals()->sum('transaction_amount'), (string)$count);
// amount spent in period
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($begin, $subDay)->setTypes([TransactionType::WITHDRAWAL])->withOpposingAccount();
$result['spent'] = bcdiv((string)$collector->getJournals()->sum('transaction_amount'), (string)$count);
// suggestion starts with the amount spent
$result['suggested'] = bcmul($result['spent'], '-1');
$result['suggested'] = 1 === bccomp($result['suggested'], $result['earned']) ? $result['earned'] : $result['suggested'];
// unless it's more than you earned. So min() of suggested/earned
$cache->store($result);
return view('budgets.info', compact('result', 'begin', 'currentEnd'));
}
/**
* @param BudgetIncomeRequest $request
*
* @return RedirectResponse
*/
public function postUpdateIncome(BudgetIncomeRequest $request): RedirectResponse
{
$start = Carbon::createFromFormat('Y-m-d', $request->string('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->string('end'));
$defaultCurrency = app('amount')->getDefaultCurrency();
$amount = $request->get('amount');
$page = 0 === $request->integer('page') ? 1 : $request->integer('page');
$this->repository->cleanupBudgets();
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
app('preferences')->mark();
return redirect(route('budgets.index', [$start->format('Y-m-d')]) . '?page=' . $page);
}
/**
* @param Request $request
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function updateIncome(Request $request, Carbon $start, Carbon $end)
{
$defaultCurrency = app('amount')->getDefaultCurrency();
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
$available = round($available, $defaultCurrency->decimal_places);
$page = (int)$request->get('page');
return view('budgets.income', compact('available', 'start', 'end', 'page'));
}
}

View File

@ -0,0 +1,106 @@
<?php
/**
* CreateController.php
* Copyright (c) 2018 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\Http\Requests\BudgetFormRequest;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use View;
/**
* Class CreateController
*/
class CreateController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Request $request
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function create(Request $request)
{
// put previous url in session if not redirect from store (not "create another").
if (true !== session('budgets.create.fromStore')) {
$this->rememberPreviousUri('budgets.create.uri');
}
$request->session()->forget('budgets.create.fromStore');
$subTitle = (string)trans('firefly.create_new_budget');
return view('budgets.create', compact('subTitle'));
}
/**
* @param BudgetFormRequest $request
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(BudgetFormRequest $request): RedirectResponse
{
$data = $request->getBudgetData();
$budget = $this->repository->store($data);
$this->repository->cleanupBudgets();
$request->session()->flash('success', (string)trans('firefly.stored_new_budget', ['name' => $budget->name]));
app('preferences')->mark();
$redirect = redirect($this->getPreviousUri('budgets.create.uri'));
if (1 === (int)$request->get('create_another')) {
// @codeCoverageIgnoreStart
$request->session()->put('budgets.create.fromStore', true);
$redirect = redirect(route('budgets.create'))->withInput();
// @codeCoverageIgnoreEnd
}
return $redirect;
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* DeleteController.php
* Copyright (c) 2018 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\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Http\Request;
use View;
/**
*
* Class DeleteController
*/
class DeleteController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Budget $budget
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function delete(Budget $budget)
{
$subTitle = trans('firefly.delete_budget', ['name' => $budget->name]);
// put previous url in session
$this->rememberPreviousUri('budgets.delete.uri');
return view('budgets.delete', compact('budget', 'subTitle'));
}
/**
* @param Request $request
* @param Budget $budget
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(Request $request, Budget $budget)
{
$name = $budget->name;
$this->repository->destroy($budget);
$request->session()->flash('success', (string)trans('firefly.deleted_budget', ['name' => $name]));
app('preferences')->mark();
return redirect($this->getPreviousUri('budgets.delete.uri'));
}
}

View File

@ -0,0 +1,117 @@
<?php
/**
* EditController.php
* Copyright (c) 2018 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\Http\Requests\BudgetFormRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use View;
/**
*
* Class EditController
*/
class EditController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Request $request
* @param Budget $budget
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function edit(Request $request, Budget $budget)
{
$subTitle = trans('firefly.edit_budget', ['name' => $budget->name]);
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
$preFilled = [
'active' => $hasOldInput ? (bool)$request->old('active') : $budget->active,
];
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('budgets.edit.fromUpdate')) {
$this->rememberPreviousUri('budgets.edit.uri');
}
$request->session()->forget('budgets.edit.fromUpdate');
$request->session()->flash('preFilled', $preFilled);
return view('budgets.edit', compact('budget', 'subTitle'));
}
/**
* @param BudgetFormRequest $request
* @param Budget $budget
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(BudgetFormRequest $request, Budget $budget): RedirectResponse
{
$data = $request->getBudgetData();
$this->repository->update($budget, $data);
$request->session()->flash('success', (string)trans('firefly.updated_budget', ['name' => $budget->name]));
$this->repository->cleanupBudgets();
app('preferences')->mark();
$redirect = redirect($this->getPreviousUri('budgets.edit.uri'));
if (1 === (int)$request->get('return_to_edit')) {
// @codeCoverageIgnoreStart
$request->session()->put('budgets.edit.fromUpdate', true);
$redirect = redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
}
return $redirect;
}
}

View File

@ -0,0 +1,174 @@
<?php
/**
* IndexController.php
* Copyright (c) 2018 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 Exception;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Log;
use Preferences;
use View;
/**
*
* Class IndexController
*/
class IndexController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Request $request
* @param string|null $moment
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(Request $request, string $moment = null)
{
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$days = 0;
$daysInMonth = 0;
// make date if present:
if (null !== $moment || '' !== (string)$moment) {
try {
$start = new Carbon($moment);
$end = app('navigation')->endOfPeriod($start, $range);
} catch (Exception $e) {
// start and end are already defined.
Log::debug(sprintf('start and end are already defined: %s', $e->getMessage()));
}
}
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
if ($today->gte($start) && $today->lte($end)) {
$days = $end->diffInDays($today);
$daysInMonth = $start->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$days = $start->diffInDays($end);
$daysInMonth = $start->diffInDays($end);
}
$days = 0 === $days ? 1 : $days;
$daysInMonth = 0 === $daysInMonth ? 1 : $daysInMonth;
$next = clone $end;
$next->addDay();
$prev = clone $start;
$prev->subDay();
$prev = app('navigation')->startOfPeriod($prev, $range);
$this->repository->cleanupBudgets();
$allBudgets = $this->repository->getActiveBudgets();
$total = $allBudgets->count();
$budgets = $allBudgets->slice(($page - 1) * $pageSize, $pageSize);
$inactive = $this->repository->getInactiveBudgets();
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
$budgetInformation = $this->repository->collectBudgetInformation($allBudgets, $start, $end);
$defaultCurrency = app('amount')->getDefaultCurrency();
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
$spent = array_sum(array_column($budgetInformation, 'spent'));
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
// paginate budgets
$budgets = new LengthAwarePaginator($budgets, $total, $pageSize, $page);
$budgets->setPath(route('budgets.index'));
// select thing for last 12 periods:
$previousLoop = [];
/** @var Carbon $previousDate */
$previousDate = clone $start;
$count = 0;
while ($count < 12) {
$previousDate->subDay();
$previousDate = app('navigation')->startOfPeriod($previousDate, $range);
$format = $previousDate->format('Y-m-d');
$previousLoop[$format] = app('navigation')->periodShow($previousDate, $range);
++$count;
}
// select thing for next 12 periods:
$nextLoop = [];
/** @var Carbon $nextDate */
$nextDate = clone $end;
$nextDate->addDay();
$count = 0;
while ($count < 12) {
$format = $nextDate->format('Y-m-d');
$nextLoop[$format] = app('navigation')->periodShow($nextDate, $range);
$nextDate = app('navigation')->endOfPeriod($nextDate, $range);
++$count;
$nextDate->addDay();
}
// display info
$currentMonth = app('navigation')->periodShow($start, $range);
$nextText = app('navigation')->periodShow($next, $range);
$prevText = app('navigation')->periodShow($prev, $range);
return view(
'budgets.index', compact(
'available', 'currentMonth', 'next', 'nextText', 'prev', 'allBudgets', 'prevText', 'periodStart', 'periodEnd', 'days', 'page',
'budgetInformation', 'daysInMonth',
'inactive', 'budgets', 'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start', 'end'
)
);
}
}

View File

@ -0,0 +1,274 @@
<?php
/**
* ShowController.php
* Copyright (c) 2018 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 FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Preferences;
use View;
/**
*
* Class ShowController
*/
class ShowController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param string|null $moment
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function noBudget(Request $request, JournalRepositoryInterface $repository, string $moment = null)
{
// default values:
$moment = $moment ?? '';
$range = Preferences::get('viewRange', '1M')->data;
$start = null;
$end = null;
$periods = new Collection;
// prep for "all" view.
if ('all' === $moment) {
$subTitle = trans('firefly.all_journals_without_budget');
$first = $repository->firstNull();
$start = null === $first ? new Carbon : $first->date;
$end = new Carbon;
}
// prep for "specific date" view.
if ('all' !== $moment && \strlen($moment) > 0) {
$start = new Carbon($moment);
/** @var Carbon $end */
$end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$periods = $this->getPeriodOverview();
}
// prep for current period
if ('' === $moment) {
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview();
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page)
->withoutBudget()->withOpposingAccount();
$transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('budgets.no-budget'));
return view('budgets.no-budget', compact('transactions', 'subTitle', 'moment', 'periods', 'start', 'end'));
}
/**
* @param Request $request
* @param Budget $budget
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function show(Request $request, Budget $budget)
{
/** @var Carbon $start */
$start = session('first', Carbon::create()->startOfYear());
$end = new Carbon;
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$limits = $this->getLimits($budget, $start, $end);
$repetition = null;
// collector:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation();
$transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('budgets.show', [$budget->id]));
$subTitle = trans('firefly.all_journals_for_budget', ['name' => $budget->name]);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'transactions', 'subTitle'));
}
/**
* @param Request $request
* @param Budget $budget
* @param BudgetLimit $budgetLimit
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws FireflyException
*/
public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit)
{
if ($budgetLimit->budget->id !== $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$subTitle = trans(
'firefly.budget_in_period',
[
'name' => $budget->name,
'start' => $budgetLimit->start_date->formatLocalized($this->monthAndDayFormat),
'end' => $budgetLimit->end_date->formatLocalized($this->monthAndDayFormat),
]
);
// collector:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($budgetLimit->start_date, $budgetLimit->end_date)
->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation();
$transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('budgets.show', [$budget->id, $budgetLimit->id]));
$start = session('first', Carbon::create()->startOfYear());
$end = new Carbon;
$limits = $this->getLimits($budget, $start, $end);
return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'transactions', 'subTitle'));
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection
{
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($budget->id);
$cache->addProperty('get-limits');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$set = $this->repository->getBudgetLimits($budget, $start, $end);
$limits = new Collection();
/** @var BudgetLimit $entry */
foreach ($set as $entry) {
$entry->spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection(), $entry->start_date, $entry->end_date);
$limits->push($entry);
}
$cache->store($limits);
return $set;
}
/**
* @return Collection
*/
private function getPeriodOverview(): Collection
{
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$first = $repository->firstNull();
$start = null === $first ? new Carbon : $first->date;
$range = Preferences::get('viewRange', '1M')->data;
$start = app('navigation')->startOfPeriod($start, $range);
$end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection;
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('no-budget-period-entries');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$dates = app('navigation')->blockPeriods($start, $end, $range);
foreach ($dates as $date) {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutBudget()->withOpposingAccount()->setTypes(
[TransactionType::WITHDRAWAL]
);
$set = $collector->getJournals();
$sum = (string)($set->sum('transaction_amount') ?? '0');
$journals = $set->count();
/** @noinspection PhpUndefinedMethodInspection */
$dateStr = $date['end']->format('Y-m-d');
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
$entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $date['end']]);
}
$cache->store($entries);
return $entries;
}
}

View File

@ -1,714 +0,0 @@
<?php
/**
* BudgetController.php
* Copyright (c) 2017 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;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Requests\BudgetFormRequest;
use FireflyIII\Http\Requests\BudgetIncomeRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
use Preferences;
use View;
/**
* Class BudgetController.
*
*/
class BudgetController extends Controller
{
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('hideBudgets', true);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Request $request
* @param BudgetRepositoryInterface $repository
* @param Budget $budget
*
* @return JsonResponse
*/
public function amount(Request $request, BudgetRepositoryInterface $repository, Budget $budget): JsonResponse
{
$amount = (string)$request->get('amount');
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
$largeDiff = false;
$warnText = '';
$days = 0;
$daysInMonth = 0;
if (0 === bccomp($amount, '0')) {
$budgetLimit = null;
}
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
Log::debug(sprintf('Start is %s, end is %s, today is %s', $start->format('Y-m-d'), $end->format('Y-m-d'), $today->format('Y-m-d')));
if ($today->gte($start) && $today->lte($end)) {
$days = $end->diffInDays($today);
$daysInMonth = $start->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$days = $start->diffInDays($end);
$daysInMonth = $start->diffInDays($end);
}
$days = 0 === $days ? 1 : $days;
$daysInMonth = 0 === $daysInMonth ? 1 : $daysInMonth;
// calculate left in budget:
$spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end);
$currency = app('amount')->getDefaultCurrency();
$left = app('amount')->formatAnything($currency, bcadd($amount, $spent), true);
$leftPerDay = 'none';
// is user has money left, calculate.
if (1 === bccomp(bcadd($amount, $spent), '0')) {
$leftPerDay = app('amount')->formatAnything($currency, bcdiv(bcadd($amount, $spent), (string)$days), true);
}
// over or under budgeting, compared to previous budgets?
$average = $this->repository->budgetedPerDay($budget);
// current average per day:
$diff = $start->diffInDays($end);
$current = $amount;
if ($diff > 0) {
$current = bcdiv($amount, (string)$diff);
}
if (bccomp(bcmul('1.1', $average), $current) === -1) {
$largeDiff = true;
$warnText = (string)trans(
'firefly.over_budget_warn',
[
'amount' => app('amount')->formatAnything($currency, $average, false),
'over_amount' => app('amount')->formatAnything($currency, $current, false),
]
);
}
app('preferences')->mark();
return response()->json(
[
'left' => $left,
'name' => $budget->name,
'limit' => $budgetLimit ? $budgetLimit->id : 0,
'amount' => $amount,
'current' => $current,
'average' => $average,
'large_diff' => $largeDiff,
'left_per_day' => $leftPerDay,
'warn_text' => $warnText,
'daysInMonth' => $daysInMonth,
]
);
}
/**
* @param Request $request
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function create(Request $request)
{
// put previous url in session if not redirect from store (not "create another").
if (true !== session('budgets.create.fromStore')) {
$this->rememberPreviousUri('budgets.create.uri');
}
$request->session()->forget('budgets.create.fromStore');
$subTitle = (string)trans('firefly.create_new_budget');
return view('budgets.create', compact('subTitle'));
}
/**
* @param Budget $budget
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function delete(Budget $budget)
{
$subTitle = trans('firefly.delete_budget', ['name' => $budget->name]);
// put previous url in session
$this->rememberPreviousUri('budgets.delete.uri');
return view('budgets.delete', compact('budget', 'subTitle'));
}
/**
* @param Request $request
* @param Budget $budget
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(Request $request, Budget $budget)
{
$name = $budget->name;
$this->repository->destroy($budget);
$request->session()->flash('success', (string)trans('firefly.deleted_budget', ['name' => $name]));
app('preferences')->mark();
return redirect($this->getPreviousUri('budgets.delete.uri'));
}
/**
* @param Request $request
* @param Budget $budget
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function edit(Request $request, Budget $budget)
{
$subTitle = trans('firefly.edit_budget', ['name' => $budget->name]);
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
$preFilled = [
'active' => $hasOldInput ? (bool)$request->old('active') : $budget->active,
];
// put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('budgets.edit.fromUpdate')) {
$this->rememberPreviousUri('budgets.edit.uri');
}
$request->session()->forget('budgets.edit.fromUpdate');
$request->session()->flash('preFilled', $preFilled);
return view('budgets.edit', compact('budget', 'subTitle'));
}
/**
* @param Request $request
* @param string|null $moment
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(Request $request, string $moment = null)
{
$range = Preferences::get('viewRange', '1M')->data;
$start = session('start', new Carbon);
$end = session('end', new Carbon);
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$days = 0;
$daysInMonth = 0;
// make date if present:
if (null !== $moment || '' !== (string)$moment) {
try {
$start = new Carbon($moment);
$end = app('navigation')->endOfPeriod($start, $range);
} catch (Exception $e) {
// start and end are already defined.
Log::debug(sprintf('start and end are already defined: %s', $e->getMessage()));
}
}
// if today is between start and end, use the diff in days between end and today (days left)
// otherwise, use diff between start and end.
$today = new Carbon;
if ($today->gte($start) && $today->lte($end)) {
$days = $end->diffInDays($today);
$daysInMonth = $start->diffInDays($today);
}
if ($today->lte($start) || $today->gte($end)) {
$days = $start->diffInDays($end);
$daysInMonth = $start->diffInDays($end);
}
$days = 0 === $days ? 1 : $days;
$daysInMonth = 0 === $daysInMonth ? 1 : $daysInMonth;
$next = clone $end;
$next->addDay();
$prev = clone $start;
$prev->subDay();
$prev = app('navigation')->startOfPeriod($prev, $range);
$this->repository->cleanupBudgets();
$allBudgets = $this->repository->getActiveBudgets();
$total = $allBudgets->count();
$budgets = $allBudgets->slice(($page - 1) * $pageSize, $pageSize);
$inactive = $this->repository->getInactiveBudgets();
$periodStart = $start->formatLocalized($this->monthAndDayFormat);
$periodEnd = $end->formatLocalized($this->monthAndDayFormat);
$budgetInformation = $this->repository->collectBudgetInformation($allBudgets, $start, $end);
$defaultCurrency = app('amount')->getDefaultCurrency();
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
$spent = array_sum(array_column($budgetInformation, 'spent'));
$budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
// paginate budgets
$budgets = new LengthAwarePaginator($budgets, $total, $pageSize, $page);
$budgets->setPath(route('budgets.index'));
// select thing for last 12 periods:
$previousLoop = [];
/** @var Carbon $previousDate */
$previousDate = clone $start;
$count = 0;
while ($count < 12) {
$previousDate->subDay();
$previousDate = app('navigation')->startOfPeriod($previousDate, $range);
$format = $previousDate->format('Y-m-d');
$previousLoop[$format] = app('navigation')->periodShow($previousDate, $range);
++$count;
}
// select thing for next 12 periods:
$nextLoop = [];
/** @var Carbon $nextDate */
$nextDate = clone $end;
$nextDate->addDay();
$count = 0;
while ($count < 12) {
$format = $nextDate->format('Y-m-d');
$nextLoop[$format] = app('navigation')->periodShow($nextDate, $range);
$nextDate = app('navigation')->endOfPeriod($nextDate, $range);
++$count;
$nextDate->addDay();
}
// display info
$currentMonth = app('navigation')->periodShow($start, $range);
$nextText = app('navigation')->periodShow($next, $range);
$prevText = app('navigation')->periodShow($prev, $range);
return view(
'budgets.index', compact(
'available', 'currentMonth', 'next', 'nextText', 'prev', 'allBudgets', 'prevText', 'periodStart', 'periodEnd', 'days', 'page',
'budgetInformation', 'daysInMonth',
'inactive', 'budgets', 'spent', 'budgeted', 'previousLoop', 'nextLoop', 'start', 'end'
)
);
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function infoIncome(Carbon $start, Carbon $end)
{
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('info-income');
Log::debug(sprintf('infoIncome start is %s', $start->format('Y-m-d')));
Log::debug(sprintf('infoIncome end is %s', $end->format('Y-m-d')));
if ($cache->has()) {
// @codeCoverageIgnoreStart
$result = $cache->get();
return view('budgets.info', compact('result'));
// @codeCoverageIgnoreEnd
}
$result = [
'available' => '0',
'earned' => '0',
'suggested' => '0',
];
$currency = app('amount')->getDefaultCurrency();
$range = Preferences::get('viewRange', '1M')->data;
/** @var Carbon $begin */
$begin = app('navigation')->subtractPeriod($start, $range, 3);
Log::debug(sprintf('Range is %s', $range));
Log::debug(sprintf('infoIncome begin is %s', $begin->format('Y-m-d')));
// get average amount available.
$total = '0';
$count = 0;
$currentStart = clone $begin;
while ($currentStart < $start) {
Log::debug(sprintf('Loop: currentStart is %s', $currentStart->format('Y-m-d')));
$currentEnd = app('navigation')->endOfPeriod($currentStart, $range);
$total = bcadd($total, $this->repository->getAvailableBudget($currency, $currentStart, $currentEnd));
$currentStart = app('navigation')->addPeriod($currentStart, $range, 0);
++$count;
}
Log::debug('Loop end');
if (0 === $count) {
$count = 1;
}
$result['available'] = bcdiv($total, (string)$count);
// amount earned in this period:
$subDay = clone $end;
$subDay->subDay();
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($begin, $subDay)->setTypes([TransactionType::DEPOSIT])->withOpposingAccount();
$result['earned'] = bcdiv((string)$collector->getJournals()->sum('transaction_amount'), (string)$count);
// amount spent in period
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($begin, $subDay)->setTypes([TransactionType::WITHDRAWAL])->withOpposingAccount();
$result['spent'] = bcdiv((string)$collector->getJournals()->sum('transaction_amount'), (string)$count);
// suggestion starts with the amount spent
$result['suggested'] = bcmul($result['spent'], '-1');
$result['suggested'] = 1 === bccomp($result['suggested'], $result['earned']) ? $result['earned'] : $result['suggested'];
// unless it's more than you earned. So min() of suggested/earned
$cache->store($result);
return view('budgets.info', compact('result', 'begin', 'currentEnd'));
}
/**
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param string|null $moment
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function noBudget(Request $request, JournalRepositoryInterface $repository, string $moment = null)
{
// default values:
$moment = $moment ?? '';
$range = Preferences::get('viewRange', '1M')->data;
$start = null;
$end = null;
$periods = new Collection;
// prep for "all" view.
if ('all' === $moment) {
$subTitle = trans('firefly.all_journals_without_budget');
$first = $repository->firstNull();
$start = null === $first ? new Carbon : $first->date;
$end = new Carbon;
}
// prep for "specific date" view.
if ('all' !== $moment && \strlen($moment) > 0) {
$start = new Carbon($moment);
/** @var Carbon $end */
$end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$periods = $this->getPeriodOverview();
}
// prep for current period
if ('' === $moment) {
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview();
$subTitle = trans(
'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page)
->withoutBudget()->withOpposingAccount();
$transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('budgets.no-budget'));
return view('budgets.no-budget', compact('transactions', 'subTitle', 'moment', 'periods', 'start', 'end'));
}
/**
* @param BudgetIncomeRequest $request
*
* @return RedirectResponse
*/
public function postUpdateIncome(BudgetIncomeRequest $request): RedirectResponse
{
$start = Carbon::createFromFormat('Y-m-d', $request->string('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->string('end'));
$defaultCurrency = app('amount')->getDefaultCurrency();
$amount = $request->get('amount');
$page = 0 === $request->integer('page') ? 1 : $request->integer('page');
$this->repository->cleanupBudgets();
$this->repository->setAvailableBudget($defaultCurrency, $start, $end, $amount);
app('preferences')->mark();
return redirect(route('budgets.index', [$start->format('Y-m-d')]) . '?page=' . $page);
}
/**
* @param Request $request
* @param Budget $budget
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function show(Request $request, Budget $budget)
{
/** @var Carbon $start */
$start = session('first', Carbon::create()->startOfYear());
$end = new Carbon;
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$limits = $this->getLimits($budget, $start, $end);
$repetition = null;
// collector:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation();
$transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('budgets.show', [$budget->id]));
$subTitle = trans('firefly.all_journals_for_budget', ['name' => $budget->name]);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'transactions', 'subTitle'));
}
/**
* @param Request $request
* @param Budget $budget
* @param BudgetLimit $budgetLimit
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws FireflyException
*/
public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit)
{
if ($budgetLimit->budget->id !== $budget->id) {
throw new FireflyException('This budget limit is not part of this budget.');
}
$page = (int)$request->get('page');
$pageSize = (int)Preferences::get('listPageSize', 50)->data;
$subTitle = trans(
'firefly.budget_in_period',
[
'name' => $budget->name,
'start' => $budgetLimit->start_date->formatLocalized($this->monthAndDayFormat),
'end' => $budgetLimit->end_date->formatLocalized($this->monthAndDayFormat),
]
);
// collector:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($budgetLimit->start_date, $budgetLimit->end_date)
->setBudget($budget)->setLimit($pageSize)->setPage($page)->withBudgetInformation();
$transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('budgets.show', [$budget->id, $budgetLimit->id]));
$start = session('first', Carbon::create()->startOfYear());
$end = new Carbon;
$limits = $this->getLimits($budget, $start, $end);
return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'transactions', 'subTitle'));
}
/**
* @param BudgetFormRequest $request
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(BudgetFormRequest $request): RedirectResponse
{
$data = $request->getBudgetData();
$budget = $this->repository->store($data);
$this->repository->cleanupBudgets();
$request->session()->flash('success', (string)trans('firefly.stored_new_budget', ['name' => $budget->name]));
app('preferences')->mark();
$redirect = redirect($this->getPreviousUri('budgets.create.uri'));
if (1 === (int)$request->get('create_another')) {
// @codeCoverageIgnoreStart
$request->session()->put('budgets.create.fromStore', true);
$redirect = redirect(route('budgets.create'))->withInput();
// @codeCoverageIgnoreEnd
}
return $redirect;
}
/**
* @param BudgetFormRequest $request
* @param Budget $budget
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(BudgetFormRequest $request, Budget $budget): RedirectResponse
{
$data = $request->getBudgetData();
$this->repository->update($budget, $data);
$request->session()->flash('success', (string)trans('firefly.updated_budget', ['name' => $budget->name]));
$this->repository->cleanupBudgets();
app('preferences')->mark();
$redirect = redirect($this->getPreviousUri('budgets.edit.uri'));
if (1 === (int)$request->get('return_to_edit')) {
// @codeCoverageIgnoreStart
$request->session()->put('budgets.edit.fromUpdate', true);
$redirect = redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]);
// @codeCoverageIgnoreEnd
}
return $redirect;
}
/**
* @param Request $request
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function updateIncome(Request $request, Carbon $start, Carbon $end)
{
$defaultCurrency = app('amount')->getDefaultCurrency();
$available = $this->repository->getAvailableBudget($defaultCurrency, $start, $end);
$available = round($available, $defaultCurrency->decimal_places);
$page = (int)$request->get('page');
return view('budgets.income', compact('available', 'start', 'end', 'page'));
}
/**
* @param Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection
{
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($budget->id);
$cache->addProperty('get-limits');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$set = $this->repository->getBudgetLimits($budget, $start, $end);
$limits = new Collection();
/** @var BudgetLimit $entry */
foreach ($set as $entry) {
$entry->spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection(), $entry->start_date, $entry->end_date);
$limits->push($entry);
}
$cache->store($limits);
return $set;
}
/**
* @return Collection
*/
private function getPeriodOverview(): Collection
{
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$first = $repository->firstNull();
$start = null === $first ? new Carbon : $first->date;
$range = Preferences::get('viewRange', '1M')->data;
$start = app('navigation')->startOfPeriod($start, $range);
$end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection;
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('no-budget-period-entries');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$dates = app('navigation')->blockPeriods($start, $end, $range);
foreach ($dates as $date) {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutBudget()->withOpposingAccount()->setTypes(
[TransactionType::WITHDRAWAL]
);
$set = $collector->getJournals();
$sum = (string)($set->sum('transaction_amount') ?? '0');
$journals = $set->count();
/** @noinspection PhpUndefinedMethodInspection */
$dateStr = $date['end']->format('Y-m-d');
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
$entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $date['end']]);
}
$cache->store($entries);
return $entries;
}
}

View File

@ -190,22 +190,31 @@ Route::group(
Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers', 'prefix' => 'budgets', 'as' => 'budgets.'], function () {
Route::get('income/{start_date}/{end_date}', ['uses' => 'BudgetController@updateIncome', 'as' => 'income']);
Route::get('info/{start_date}/{end_date}', ['uses' => 'BudgetController@infoIncome', 'as' => 'income.info']);
Route::get('create', ['uses' => 'BudgetController@create', 'as' => 'create']);
Route::get('edit/{budget}', ['uses' => 'BudgetController@edit', 'as' => 'edit']);
Route::get('delete/{budget}', ['uses' => 'BudgetController@delete', 'as' => 'delete']);
Route::get('show/{budget}', ['uses' => 'BudgetController@show', 'as' => 'show']);
Route::get('show/{budget}/{budgetLimit}', ['uses' => 'BudgetController@showByBudgetLimit', 'as' => 'show.limit']);
Route::get('list/no-budget/{moment?}', ['uses' => 'BudgetController@noBudget', 'as' => 'no-budget']);
Route::get('{moment?}', ['uses' => 'BudgetController@index', 'as' => 'index']);
// 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', ['uses' => 'Budget\CreateController@create', 'as' => 'create']);
Route::post('store', ['uses' => 'Budget\CreateController@store', 'as' => 'store']);
Route::post('income', ['uses' => 'BudgetController@postUpdateIncome', 'as' => 'income.post']);
Route::post('store', ['uses' => 'BudgetController@store', 'as' => 'store']);
Route::post('update/{budget}', ['uses' => 'BudgetController@update', 'as' => 'update']);
Route::post('destroy/{budget}', ['uses' => 'BudgetController@destroy', 'as' => 'destroy']);
Route::post('amount/{budget}', ['uses' => 'BudgetController@amount', 'as' => 'amount']);
// edit
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/{moment?}', ['uses' => 'Budget\ShowController@noBudget', 'as' => 'no-budget']);
// index
Route::get('{moment?}', ['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::get('info/{start_date}/{end_date}', ['uses' => 'Budget\AmountController@infoIncome', 'as' => 'income.info']);
Route::post('income', ['uses' => 'Budget\AmountController@postUpdateIncome', 'as' => 'income.post']);
Route::post('amount/{budget}', ['uses' => 'Budget\AmountController@amount', 'as' => 'amount']);
}
);

View File

@ -0,0 +1,220 @@
<?php
/**
* AmountControllerTest.php
* Copyright (c) 2018 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 Tests\Feature\Controllers\Budget;
use Carbon\Carbon;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Tests\TestCase;
/**
*
* Class AmountControllerTest
*/
class AmountControllerTest extends TestCase
{
/**
*
*/
public function setUp(): void
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
*/
public function testAmount(): void
{
Log::debug('Now in testAmount()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$data = ['amount' => 200, 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
*/
public function testAmountLargeDiff(): void
{
Log::debug('Now in testAmount()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$data = ['amount' => 20000, 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
$response->assertSee('Normally you budget about \u20ac10.00 per day.');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
*/
public function testAmountOutOfRange(): void
{
Log::debug('Now in testAmountOutOfRange()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$today = new Carbon;
$start = $today->startOfMonth()->format('Y-m-d');
$end = $today->endOfMonth()->format('Y-m-d');
$data = ['amount' => 200, 'start' => $start, 'end' => $end];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
*/
public function testAmountZero(): void
{
Log::debug('Now in testAmountZero()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$data = ['amount' => 0, 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
*/
public function testInfoIncome(): void
{
Log::debug('Now in testInfoIncome()');
// mock stuff
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$accountRepos->shouldReceive('setUser');
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$this->be($this->user());
$response = $this->get(route('budgets.income.info', ['20170101', '20170131']));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testInfoIncomeExpanded(string $range): void
{
Log::debug(sprintf('Now in testInfoIncomeExpanded(%s)', $range));
// mock stuff
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$accountRepos->shouldReceive('setUser');
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.income.info', ['20170301', '20170430']));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
*/
public function testPostUpdateIncome(): void
{
Log::debug('Now in testPostUpdateIncome()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('setAvailableBudget');
$repository->shouldReceive('cleanupBudgets');
$data = ['amount' => '200', 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.income.post'), $data);
$response->assertStatus(302);
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\AmountController
*/
public function testUpdateIncome(): void
{
Log::debug('Now in testUpdateIncome()');
// must be in list
$this->be($this->user());
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('getAvailableBudget')->andReturn('1');
$repository->shouldReceive('cleanupBudgets');
$response = $this->get(route('budgets.income', ['2017-01-01', '2017-01-31']));
$response->assertStatus(200);
}
}

View File

@ -0,0 +1,95 @@
<?php
/**
* CreateControllerTest.php
* Copyright (c) 2018 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 Tests\Feature\Controllers\Budget;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Log;
use Tests\TestCase;
/**
*
* Class CreateControllerTest
*/
class CreateControllerTest extends TestCase
{
/**
*
*/
public function setUp(): void
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\CreateController
*/
public function testCreate(): void
{
Log::debug('Now in testCreate()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.create'));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\CreateController
*/
public function testStore(): void
{
Log::debug('Now in testStore()');
// mock stuff
$budget = factory(Budget::class)->make();
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('findNull')->andReturn($budget);
$repository->shouldReceive('store')->andReturn($budget);
$repository->shouldReceive('cleanupBudgets');
$this->session(['budgets.create.uri' => 'http://localhost']);
$data = [
'name' => 'New Budget ' . random_int(1000, 9999),
];
$this->be($this->user());
$response = $this->post(route('budgets.store'), $data);
$response->assertStatus(302);
$response->assertSessionHas('success');
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* DeleteControllerTest.php
* Copyright (c) 2018 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 Tests\Feature\Controllers\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Log;
use Tests\TestCase;
/**
*
* Class DeleteControllerTest
*/
class DeleteControllerTest extends TestCase
{
/**
*
*/
public function setUp(): void
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\DeleteController
*/
public function testDelete(): void
{
Log::debug('Now in testDelete()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.delete', [1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\DeleteController
*/
public function testDestroy(): void
{
Log::debug('Now in testDestroy()');
// mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('destroy')->andReturn(true);
$this->session(['budgets.delete.uri' => 'http://localhost']);
$this->be($this->user());
$response = $this->post(route('budgets.destroy', [1]));
$response->assertStatus(302);
$response->assertSessionHas('success');
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* EditControllerTest.php
* Copyright (c) 2018 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 Tests\Feature\Controllers\Budget;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Log;
use Tests\TestCase;
/**
*
* Class EditControllerTest
*/
class EditControllerTest extends TestCase
{
/**
*
*/
public function setUp(): void
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\EditController
*/
public function testEdit(): void
{
Log::debug('Now in testEdit()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.edit', [1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\EditController
*/
public function testUpdate(): void
{
Log::debug('Now in testUpdate()');
// mock stuff
$budget = factory(Budget::class)->make();
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('findNull')->andReturn($budget);
$repository->shouldReceive('update');
$repository->shouldReceive('cleanupBudgets');
$this->session(['budgets.edit.uri' => 'http://localhost']);
$data = [
'name' => 'Updated Budget ' . random_int(1000, 9999),
'active' => 1,
];
$this->be($this->user());
$response = $this->post(route('budgets.update', [1]), $data);
$response->assertStatus(302);
$response->assertSessionHas('success');
}
}

View File

@ -0,0 +1,237 @@
<?php
/**
* IndexControllerTest.php
* Copyright (c) 2018 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 Tests\Feature\Controllers\Budget;
use Carbon\Carbon;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Tests\TestCase;
/**
*
* Class IndexControllerTest
*/
class IndexControllerTest extends TestCase
{
/**
*
*/
public function setUp(): void
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\IndexController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndex(string $range): void
{
Log::debug(sprintf('Now in testIndex(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index'));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\IndexController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndexOutOfRange(string $range): void
{
Log::debug(sprintf('Now in testIndexOutOfRange(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$this->be($this->user());
$today = new Carbon;
$today->startOfMonth();
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index', [$today->format('Y-m-d')]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\IndexController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndexWithDate(string $range): void
{
Log::debug(sprintf('Now in testIndexWithDate(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index', ['2017-01-01']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\IndexController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndexWithInvalidDate(string $range): void
{
Log::debug(sprintf('Now in testIndexWithInvalidDate(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index', ['Hello-there']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
}

View File

@ -0,0 +1,265 @@
<?php
/**
* ShowControllerTest.php
* Copyright (c) 2018 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 Tests\Feature\Controllers\Budget;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
use Tests\TestCase;
/**
*
* Class ShowControllerTest
*/
class ShowControllerTest extends TestCase
{
/**
*
*/
public function setUp(): void
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\ShowController
* @dataProvider dateRangeProvider
*
* @param string $range
*
*/
public function testNoBudget(string $range): void
{
Log::debug(sprintf('Now in testNoBudget(%s)', $range));
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$collector = $this->mock(JournalCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('getJournals')->andReturn(new Collection);
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('withoutBudget')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$date = new Carbon();
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.no-budget'));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\ShowController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testNoBudgetAll(string $range): void
{
Log::debug(sprintf('Now in testNoBudgetAll(%s)', $range));
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$collector = $this->mock(JournalCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('withoutBudget')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('getJournals')->andReturn(new Collection);
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$date = new Carbon();
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.no-budget', ['all']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\ShowController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testNoBudgetDate(string $range): void
{
Log::debug(sprintf('Now in testNoBudgetDate(%s)', $range));
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$collector = $this->mock(JournalCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('getJournals')->andReturn(new Collection);
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('withoutBudget')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$date = new Carbon();
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.no-budget', ['2016-01-01']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\ShowController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testShow(string $range): void
{
Log::debug(sprintf('Now in testShow(%s)', $range));
// mock stuff
$budgetLimit = factory(BudgetLimit::class)->make();
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$collector = $this->mock(JournalCollectorInterface::class);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setBudget')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$collector->shouldReceive('withBudgetInformation')->andReturnSelf();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository = $this->mock(BudgetRepositoryInterface::class);
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$date = new Carbon();
$date->subDay();
$this->session(['first' => $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.show', [1]));
$response->assertStatus(200);
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\ShowController
* @expectedExceptionMessage This budget limit is not part of
*/
public function testShowByBadBudgetLimit(): void
{
Log::debug('Now in testShowByBadBudgetLimit()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.show.limit', [1, 8]));
$response->assertStatus(500);
}
/**
* @covers \FireflyIII\Http\Controllers\Budget\ShowController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testShowByBudgetLimit(string $range): void
{
Log::debug(sprintf('Now in testShowByBudgetLimit(%s)', $range));
// mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
// mock account repository
$accountRepository = $this->mock(AccountRepositoryInterface::class);
$accountRepository->shouldReceive('getAccountsByType')->andReturn(new Collection);
// mock budget repository
$budgetRepository = $this->mock(BudgetRepositoryInterface::class);
$budgetRepository->shouldReceive('spentInPeriod')->andReturn('1');
$budgetRepository->shouldReceive('getBudgetLimits')->andReturn(new Collection);
// mock journal collector:
$collector = $this->mock(JournalCollectorInterface::class);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setBudget')->andReturnSelf();
$collector->shouldReceive('withBudgetInformation')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.show.limit', [1, 1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
}

View File

@ -1,756 +0,0 @@
<?php
/**
* BudgetControllerTest.php
* Copyright (c) 2017 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 Tests\Feature\Controllers;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
use Tests\TestCase;
/**
* Class BudgetControllerTest
*
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class BudgetControllerTest extends TestCase
{
/**
*
*/
public function setUp()
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::amount
*/
public function testAmount(): void
{
Log::debug('Now in testAmount()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$data = ['amount' => 200, 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::amount
*/
public function testAmountLargeDiff(): void
{
Log::debug('Now in testAmount()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$data = ['amount' => 20000, 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
$response->assertSee('Normally you budget about \u20ac10.00 per day.');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::amount
*/
public function testAmountZero(): void
{
Log::debug('Now in testAmountZero()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$data = ['amount' => 0, 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::amount
*/
public function testAmountOutOfRange(): void
{
Log::debug('Now in testAmountOutOfRange()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('updateLimitAmount')->andReturn(new BudgetLimit);
$repository->shouldReceive('spentInPeriod')->andReturn('0');
$repository->shouldReceive('budgetedPerDay')->andReturn('10');
$today = new Carbon;
$start = $today->startOfMonth()->format('Y-m-d');
$end = $today->endOfMonth()->format('Y-m-d');
$data = ['amount' => 200, 'start' => $start, 'end' => $end];
$this->be($this->user());
$response = $this->post(route('budgets.amount', [1]), $data);
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::create
*/
public function testCreate(): void
{
Log::debug('Now in testCreate()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.create'));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::delete
*/
public function testDelete(): void
{
Log::debug('Now in testDelete()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.delete', [1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::destroy
*/
public function testDestroy(): void
{
Log::debug('Now in testDestroy()');
// mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('destroy')->andReturn(true);
$this->session(['budgets.delete.uri' => 'http://localhost']);
$this->be($this->user());
$response = $this->post(route('budgets.destroy', [1]));
$response->assertStatus(302);
$response->assertSessionHas('success');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::edit
*/
public function testEdit(): void
{
Log::debug('Now in testEdit()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.edit', [1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::index
* @covers \FireflyIII\Http\Controllers\BudgetController::__construct
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndex(string $range): void
{
Log::debug(sprintf('Now in testIndex(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index'));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::index
* @covers \FireflyIII\Http\Controllers\BudgetController::__construct
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndexWithDate(string $range): void
{
Log::debug(sprintf('Now in testIndexWithDate(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index', ['2017-01-01']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::index
* @covers \FireflyIII\Http\Controllers\BudgetController::__construct
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndexOutOfRange(string $range): void
{
Log::debug(sprintf('Now in testIndexOutOfRange(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$this->be($this->user());
$today = new Carbon;
$today->startOfMonth();
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index', [$today->format('Y-m-d')]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::index
* @covers \FireflyIII\Http\Controllers\BudgetController::__construct
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testIndexWithInvalidDate(string $range): void
{
Log::debug(sprintf('Now in testIndexWithInvalidDate(%s)', $range));
// mock stuff
$budget = factory(Budget::class)->make();
$budgetLimit = factory(BudgetLimit::class)->make();
// set budget limit to current month:
$budgetLimit->start_date = Carbon::now()->startOfMonth();
$budgetLimit->end_date = Carbon::now()->endOfMonth();
$budgetInfo = [
$budget->id => [
'spent' => '0',
'budgeted' => '0',
'currentRep' => false,
],
];
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository->shouldReceive('cleanupBudgets');
$repository->shouldReceive('getActiveBudgets')->andReturn(new Collection([$budget]));
$repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('collectBudgetInformation')->andReturn($budgetInfo);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.index', ['Hello-there']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::infoIncome
*/
public function testInfoIncome(): void
{
Log::debug('Now in testInfoIncome()');
// mock stuff
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$accountRepos->shouldReceive('setUser');
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$this->be($this->user());
$response = $this->get(route('budgets.income.info', ['20170101', '20170131']));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::infoIncome
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testInfoIncomeExpanded(string $range): void
{
Log::debug(sprintf('Now in testInfoIncomeExpanded(%s)', $range));
// mock stuff
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(BudgetRepositoryInterface::class);
$repository->shouldReceive('getAvailableBudget')->andReturn('100.123');
$accountRepos->shouldReceive('setUser');
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.income.info', ['20170301', '20170430']));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::noBudget
* @covers \FireflyIII\Http\Controllers\BudgetController::getPeriodOverview
* @dataProvider dateRangeProvider
*
* @param string $range
*
*/
public function testNoBudget(string $range): void
{
Log::debug(sprintf('Now in testNoBudget(%s)', $range));
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$collector = $this->mock(JournalCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('getJournals')->andReturn(new Collection);
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('withoutBudget')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$date = new Carbon();
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.no-budget'));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::noBudget
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testNoBudgetAll(string $range): void
{
Log::debug(sprintf('Now in testNoBudgetAll(%s)', $range));
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$collector = $this->mock(JournalCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('withoutBudget')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('getJournals')->andReturn(new Collection);
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$date = new Carbon();
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.no-budget', ['all']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::noBudget
* @covers \FireflyIII\Http\Controllers\BudgetController::getPeriodOverview
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testNoBudgetDate(string $range): void
{
Log::debug(sprintf('Now in testNoBudgetDate(%s)', $range));
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$collector = $this->mock(JournalCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('getJournals')->andReturn(new Collection);
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('withoutBudget')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$date = new Carbon();
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.no-budget', ['2016-01-01']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::postUpdateIncome
*/
public function testPostUpdateIncome(): void
{
Log::debug('Now in testPostUpdateIncome()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('setAvailableBudget');
$repository->shouldReceive('cleanupBudgets');
$data = ['amount' => '200', 'start' => '2017-01-01', 'end' => '2017-01-31'];
$this->be($this->user());
$response = $this->post(route('budgets.income.post'), $data);
$response->assertStatus(302);
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::show
* @covers \FireflyIII\Http\Controllers\BudgetController::getLimits
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testShow(string $range): void
{
Log::debug(sprintf('Now in testShow(%s)', $range));
// mock stuff
$budgetLimit = factory(BudgetLimit::class)->make();
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$collector = $this->mock(JournalCollectorInterface::class);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setBudget')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$collector->shouldReceive('withBudgetInformation')->andReturnSelf();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
$repository = $this->mock(BudgetRepositoryInterface::class);
$repository->shouldReceive('getBudgetLimits')->andReturn(new Collection([$budgetLimit]));
$repository->shouldReceive('spentInPeriod')->andReturn('-1');
$date = new Carbon();
$date->subDay();
$this->session(['first' => $date]);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.show', [1]));
$response->assertStatus(200);
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::showByBudgetLimit
* @expectedExceptionMessage This budget limit is not part of
*/
public function testShowByBadBudgetLimit(): void
{
Log::debug('Now in testShowByBadBudgetLimit()');
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user());
$response = $this->get(route('budgets.show.limit', [1, 8]));
$response->assertStatus(500);
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::showByBudgetLimit()
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testShowByBudgetLimit(string $range): void
{
Log::debug(sprintf('Now in testShowByBudgetLimit(%s)', $range));
// mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
// mock account repository
$accountRepository = $this->mock(AccountRepositoryInterface::class);
$accountRepository->shouldReceive('getAccountsByType')->andReturn(new Collection);
// mock budget repository
$budgetRepository = $this->mock(BudgetRepositoryInterface::class);
$budgetRepository->shouldReceive('spentInPeriod')->andReturn('1');
$budgetRepository->shouldReceive('getBudgetLimits')->andReturn(new Collection);
// mock journal collector:
$collector = $this->mock(JournalCollectorInterface::class);
$collector->shouldReceive('setAllAssetAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setBudget')->andReturnSelf();
$collector->shouldReceive('withBudgetInformation')->andReturnSelf();
$collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10));
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('budgets.show.limit', [1, 1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::store
*/
public function testStore(): void
{
Log::debug('Now in testStore()');
// mock stuff
$budget = factory(Budget::class)->make();
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('findNull')->andReturn($budget);
$repository->shouldReceive('store')->andReturn($budget);
$repository->shouldReceive('cleanupBudgets');
$this->session(['budgets.create.uri' => 'http://localhost']);
$data = [
'name' => 'New Budget ' . random_int(1000, 9999),
];
$this->be($this->user());
$response = $this->post(route('budgets.store'), $data);
$response->assertStatus(302);
$response->assertSessionHas('success');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::update
*/
public function testUpdate(): void
{
Log::debug('Now in testUpdate()');
// mock stuff
$budget = factory(Budget::class)->make();
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('findNull')->andReturn($budget);
$repository->shouldReceive('update');
$repository->shouldReceive('cleanupBudgets');
$this->session(['budgets.edit.uri' => 'http://localhost']);
$data = [
'name' => 'Updated Budget ' . random_int(1000, 9999),
'active' => 1,
];
$this->be($this->user());
$response = $this->post(route('budgets.update', [1]), $data);
$response->assertStatus(302);
$response->assertSessionHas('success');
}
/**
* @covers \FireflyIII\Http\Controllers\BudgetController::updateIncome
*/
public function testUpdateIncome(): void
{
Log::debug('Now in testUpdateIncome()');
// must be in list
$this->be($this->user());
// mock stuff
$repository = $this->mock(BudgetRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('getAvailableBudget')->andReturn('1');
$repository->shouldReceive('cleanupBudgets');
$response = $this->get(route('budgets.income', ['2017-01-01', '2017-01-31']));
$response->assertStatus(200);
}
}

View File

@ -35,7 +35,7 @@ class FromAccountStartsTest extends TestCase
/**
*
*/
public function setUp()
public function setUp(): void
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
@ -54,17 +54,23 @@ class FromAccountStartsTest extends TestCase
$transaction = $journal->transactions()->where('amount', '<', 0)->first();
$account = $transaction->account;
$count = $journal->transactions()->count();
$name = $account->name ?? '';
Log::debug(sprintf('Loop: %d, transaction count: %d, account is null: %d', $loops, $count, (int)null===$account));
Log::debug(sprintf('Loop: %d, transaction count: %d, account is null: %d, name = "%s"', $loops, $count, (int)null === $account, $name));
$loops++;
// do this until: account is not null, journal has two transactions, loops is below 30
} while (!(null !== $account && 2 === $count && $loops < 30));
// do this while the following is untrue:
// 1) account is not null,
// 2) journal has two transactions
// 3) loops is less than 30
// 4) $name is longer than 3
} while (!(null !== $account && 2 === $count && $loops < 30 && \strlen($name) > 3));
$trigger = FromAccountStarts::makeFromStrings(substr($account->name, 0, -3), false);
$result = $trigger->triggered($journal);
$this->assertTrue($result);
exit;
}
/**
@ -80,13 +86,18 @@ class FromAccountStartsTest extends TestCase
$transaction = $journal->transactions()->where('amount', '<', 0)->first();
$account = $transaction->account;
$count = $journal->transactions()->count();
$name = $account->name ?? '';
Log::debug(sprintf('Loop: %d, transaction count: %d, account is null: %d', $loops, $count, (int)null===$account));
Log::debug(sprintf('Loop: %d, transaction count: %d, account is null: %d, name = "%s"', $loops, $count, (int)null === $account, $name));
$loops++;
// do this until: account is not null, journal has two transactions, loops is below 30
} while (!(null !== $account && 2 === $count && $loops < 30));
// do this while the following is untrue:
// 1) account is not null,
// 2) journal has two transactions
// 3) loops is less than 30
// 4) $name is longer than 3
} while (!(null !== $account && 2 === $count && $loops < 30 && \strlen($name) > 3));
$trigger = FromAccountStarts::makeFromStrings('bla-bla-bla' . $account->name, false);
$result = $trigger->triggered($journal);