From ce06fb73b117d569ea17bc2adef5e95397c264b4 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 1 Sep 2019 15:23:33 +0200 Subject: [PATCH] Expand view of report and make multi currency --- app/Helpers/Report/PopupReport.php | 29 ++++++++-- app/Helpers/Report/PopupReportInterface.php | 5 +- .../Budget/AvailableBudgetController.php | 37 +++++++----- .../Budget/BudgetLimitController.php | 4 -- .../Controllers/Popup/ReportController.php | 3 + .../Http/Controllers/RenderPartialViews.php | 45 ++++++++++++--- resources/views/v1/budgets/income.twig | 31 ---------- resources/views/v1/budgets/info.twig | 53 ------------------ resources/views/v1/budgets/show.twig | 2 - .../views/v1/reports/partials/balance.twig | 56 ++++++++++--------- .../v1/reports/partials/budget-period.twig | 1 - .../views/v1/reports/partials/categories.twig | 3 +- routes/web.php | 2 +- 13 files changed, 124 insertions(+), 147 deletions(-) delete mode 100644 resources/views/v1/budgets/income.twig delete mode 100644 resources/views/v1/budgets/info.twig diff --git a/app/Helpers/Report/PopupReport.php b/app/Helpers/Report/PopupReport.php index 397db5941e..8c7d36ca53 100644 --- a/app/Helpers/Report/PopupReport.php +++ b/app/Helpers/Report/PopupReport.php @@ -81,6 +81,16 @@ class PopupReport implements PopupReportInterface */ public function balanceForNoBudget(Account $account, array $attributes): array { + // filter by currency, if set. + $currencyId = $attributes['currencyId'] ?? null; + $currency = null; + if (null !== $currencyId) { + /** @var CurrencyRepositoryInterface $repos */ + $repos = app(CurrencyRepositoryInterface::class); + $currency = $repos->find((int)$currencyId); + } + + /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector @@ -91,6 +101,10 @@ class PopupReport implements PopupReportInterface ->setRange($attributes['startDate'], $attributes['endDate']) ->withoutBudget(); + if (null !== $currency) { + $collector->setCurrency($currency); + } + return $collector->getExtractedJournals(); } @@ -139,12 +153,12 @@ class PopupReport implements PopupReportInterface /** * Collect journals by a category. * - * @param Category $category + * @param Category|null $category * @param array $attributes * * @return array */ - public function byCategory(Category $category, array $attributes): array + public function byCategory(?Category $category, array $attributes): array { // filter by currency, if set. $currencyId = $attributes['currencyId'] ?? null; @@ -163,8 +177,15 @@ class PopupReport implements PopupReportInterface ->withAccountInformation() ->withBudgetInformation() ->withCategoryInformation() - ->setRange($attributes['startDate'], $attributes['endDate'])->withAccountInformation() - ->setCategory($category); + ->setRange($attributes['startDate'], $attributes['endDate'])->withAccountInformation(); + + if(null!== $category) { + $collector->setCategory($category); + } + if(null === $category) { + $collector->withoutCategory(); + } + if (null !== $currency) { $collector->setCurrency($currency); } diff --git a/app/Helpers/Report/PopupReportInterface.php b/app/Helpers/Report/PopupReportInterface.php index d8b05f093c..c24f5d12bc 100644 --- a/app/Helpers/Report/PopupReportInterface.php +++ b/app/Helpers/Report/PopupReportInterface.php @@ -32,6 +32,7 @@ use Illuminate\Support\Collection; */ interface PopupReportInterface { + /** * Get balances for budget. * @@ -66,12 +67,12 @@ interface PopupReportInterface /** * Group by category. * - * @param Category $category + * @param Category|null $category * @param array $attributes * * @return array */ - public function byCategory(Category $category, array $attributes): array; + public function byCategory(?Category $category, array $attributes): array; /** * Do something with expense. Sorry, I am not very inspirational here. diff --git a/app/Http/Controllers/Budget/AvailableBudgetController.php b/app/Http/Controllers/Budget/AvailableBudgetController.php index c6c3f60ddc..bdfb94c125 100644 --- a/app/Http/Controllers/Budget/AvailableBudgetController.php +++ b/app/Http/Controllers/Budget/AvailableBudgetController.php @@ -30,9 +30,6 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; -use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use Illuminate\Http\Request; use Log; @@ -46,14 +43,8 @@ class AvailableBudgetController extends Controller /** @var AvailableBudgetRepositoryInterface */ private $abRepository; - /** @var BudgetLimitRepositoryInterface */ - private $blRepository; /** @var CurrencyRepositoryInterface */ private $currencyRepos; - /** @var OperationsRepositoryInterface */ - private $opsRepository; - /** @var BudgetRepositoryInterface The budget repository */ - private $repository; /** * AmountController constructor. @@ -68,10 +59,7 @@ class AvailableBudgetController extends Controller function ($request, $next) { app('view')->share('title', (string)trans('firefly.budgets')); app('view')->share('mainTitleIcon', 'fa-tasks'); - $this->repository = app(BudgetRepositoryInterface::class); - $this->opsRepository = app(OperationsRepositoryInterface::class); $this->abRepository = app(AvailableBudgetRepositoryInterface::class); - $this->blRepository = app(BudgetLimitRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class); return $next($request); @@ -83,6 +71,13 @@ class AvailableBudgetController extends Controller * Create will always assume the user's default currency, if it's not set. * * This method will check if there is no AB, and refuse to continue if it exists. + * + * @param Request $request + * @param Carbon $start + * @param Carbon $end + * @param TransactionCurrency|null $currency + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View */ public function create(Request $request, Carbon $start, Carbon $end, ?TransactionCurrency $currency = null) { @@ -106,10 +101,16 @@ class AvailableBudgetController extends Controller /** * createAlternative will show a list of enabled currencies so the user can pick one. + * + * @param Request $request + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function createAlternative(Request $request, Carbon $start, Carbon $end) { - $currencies = $this->currencyRepos->getEnabled(); + $currencies = $this->currencyRepos->getEnabled(); $availableBudgets = $this->abRepository->get($start, $end); // remove already budgeted currencies: @@ -121,12 +122,14 @@ class AvailableBudgetController extends Controller return false; } } + return true; } ); - $page = (int)($request->get('page') ?? 1); + $page = (int)($request->get('page') ?? 1); + return view('budgets.available-budgets.create-alternative', compact('start', 'end', 'page', 'currencies')); } @@ -145,6 +148,8 @@ class AvailableBudgetController extends Controller /** * @param AvailableBudget $availableBudget + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function edit(AvailableBudget $availableBudget) { @@ -153,6 +158,8 @@ class AvailableBudgetController extends Controller /** * @param Request $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function store(Request $request) { @@ -197,6 +204,8 @@ class AvailableBudgetController extends Controller /** * @param Request $request * @param AvailableBudget $availableBudget + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function update(Request $request, AvailableBudget $availableBudget) { diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index 2333e952e8..85411c6252 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -31,7 +31,6 @@ use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; @@ -50,8 +49,6 @@ class BudgetLimitController extends Controller { use DateCalculation; - /** @var AvailableBudgetRepositoryInterface */ - private $abRepository; /** @var BudgetLimitRepositoryInterface */ private $blRepository; /** @var CurrencyRepositoryInterface */ @@ -73,7 +70,6 @@ class BudgetLimitController extends Controller app('view')->share('mainTitleIcon', 'fa-tasks'); $this->repository = app(BudgetRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class); - $this->abRepository = app(AvailableBudgetRepositoryInterface::class); $this->blRepository = app(BudgetLimitRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class); diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index 69405f6d00..6915a6581e 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -68,6 +68,9 @@ class ReportController extends Controller case 'category-entry': $html = $this->categoryEntry($attributes); break; + case 'budget-entry': + $html = $this->budgetEntry($attributes); + break; } return response()->json(['html' => $html]); diff --git a/app/Support/Http/Controllers/RenderPartialViews.php b/app/Support/Http/Controllers/RenderPartialViews.php index b97ce71663..9678c8e2c0 100644 --- a/app/Support/Http/Controllers/RenderPartialViews.php +++ b/app/Support/Http/Controllers/RenderPartialViews.php @@ -75,6 +75,38 @@ trait RenderPartialViews return $result; } + /** + * View for transactions in a budget for an account. + * + * @param array $attributes + * + * @return string + */ + protected function budgetEntry(array $attributes): string // generate view for report. + { + /** @var PopupReportInterface $popupHelper */ + $popupHelper = app(PopupReportInterface::class); + + /** @var BudgetRepositoryInterface $budgetRepository */ + $budgetRepository = app(BudgetRepositoryInterface::class); + $budget = $budgetRepository->findNull((int)$attributes['budgetId']); + + $accountRepos = app(AccountRepositoryInterface::class); + $account = $accountRepos->findNull((int)$attributes['accountId']); + + $journals = $popupHelper->balanceForBudget($budget, $account, $attributes); + // @codeCoverageIgnoreStart + try { + $view = view('popup.report.balance-amount', compact('journals', 'budget','account'))->render(); + } catch (Throwable $e) { + Log::error(sprintf('Could not render: %s', $e->getMessage())); + $view = 'Firefly III could not render the view. Please see the log files.'; + } + + // @codeCoverageIgnoreEnd + + return $view; + } /** * Get options for budget report. @@ -126,6 +158,7 @@ trait RenderPartialViews Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; } + // @codeCoverageIgnoreEnd return $view; @@ -146,12 +179,7 @@ trait RenderPartialViews /** @var CategoryRepositoryInterface $categoryRepository */ $categoryRepository = app(CategoryRepositoryInterface::class); $category = $categoryRepository->findNull((int)$attributes['categoryId']); - - if (null === $category) { - return 'This is an unknown category. Apologies.'; - } - - $journals = $popupHelper->byCategory($category, $attributes); + $journals = $popupHelper->byCategory($category, $attributes); // @codeCoverageIgnoreStart try { $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); @@ -159,6 +187,7 @@ trait RenderPartialViews Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; } + // @codeCoverageIgnoreEnd return $view; @@ -216,6 +245,7 @@ trait RenderPartialViews Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; } + // @codeCoverageIgnoreEnd return $view; @@ -314,7 +344,7 @@ trait RenderPartialViews /** @var PopupReportInterface $popupHelper */ $popupHelper = app(PopupReportInterface::class); - $account = $accountRepository->findNull((int)$attributes['accountId']); + $account = $accountRepository->findNull((int)$attributes['accountId']); if (null === $account) { return 'This is an unknown category. Apologies.'; @@ -328,6 +358,7 @@ trait RenderPartialViews Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; } + // @codeCoverageIgnoreEnd return $view; diff --git a/resources/views/v1/budgets/income.twig b/resources/views/v1/budgets/income.twig deleted file mode 100644 index 8f173205b4..0000000000 --- a/resources/views/v1/budgets/income.twig +++ /dev/null @@ -1,31 +0,0 @@ -<div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal"><span>×</span><span class="sr-only">{{ 'close'|_ }}</span> - </button> - <h4 class="modal-title"> - {{ trans('firefly.update_budget_amount_range', - {start: start.formatLocalized(monthAndDayFormat), end: end.formatLocalized(monthAndDayFormat)}) }} - </h4> - </div> - - <form style="display: inline;" id="income" action="{{ route('budgets.income.post') }}" method="POST"> - <div class="modal-body"> - <input type="hidden" name="_token" value="{{ csrf_token() }}"/> - <input type="hidden" name="start" value="{{ start.format('Y-m-d') }}"/> - <input type="hidden" name="end" value="{{ end.format('Y-m-d') }}"/> - <input type="hidden" name="page" value="{{ page }}" /> - - <div class="input-group"> - <div class="input-group-addon">{{ defaultCurrency.symbol|raw }}</div> - <input step="any" class="form-control" id="amount" value="{{ available }}" autocomplete="off" name="amount" type="number"/> - </div> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button> - <button type="submit" class="btn btn-primary">{{ 'update_amount'|_ }}</button> - </div> - </form> - </div> -</div> - diff --git a/resources/views/v1/budgets/info.twig b/resources/views/v1/budgets/info.twig deleted file mode 100644 index dc8b3acf0c..0000000000 --- a/resources/views/v1/budgets/info.twig +++ /dev/null @@ -1,53 +0,0 @@ -<div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal"><span>×</span><span class="sr-only">{{ 'close'|_ }}</span> - </button> - <h4 class="modal-title"> - {{ trans('firefly.info_on_available_amount') }} - </h4> - </div> - - <div class="modal-body"> - <p> - {{ 'available_amount_indication'|_ }} - </p> - <table class="table table-bordered table-striped"> - <tr> - <td> - {{ 'budgeted'|_ }} - <small><br/> - {{ trans('firefly.average_between', {start:searchBegin.formatLocalized(monthAndDayFormat), end:searchEnd.formatLocalized(monthAndDayFormat)}) }} - </td> - <td> - <span class="pull-right">{{ result.available|formatAmount }}</span> - </td> - </tr> - <tr> - <td>{{ 'earned'|_ }} - <small><br/> - {{ trans('firefly.average_between', {start:searchBegin.formatLocalized(monthAndDayFormat), end:searchEnd.formatLocalized(monthAndDayFormat)}) }} - </small> - </td> - <td><span class="pull-right">{{ result.earned|formatAmount }}</span></td> - </tr> - <tr> - <td>{{ 'spent'|_ }} - <small><br/> - {{ trans('firefly.average_between', {start:searchBegin.formatLocalized(monthAndDayFormat), end:searchEnd.formatLocalized(monthAndDayFormat)}) }} - </small> - </td> - <td><span class="pull-right">{{ result.spent|formatAmount }}</span></td> - </tr> - <tr> - <td><strong>{{ 'suggested'|_ }}</strong></td> - <td><span class="pull-right">{{ result.suggested|formatAmount }}</span></td> - </tr> - </table> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button> - </div> - </div> -</div> - diff --git a/resources/views/v1/budgets/show.twig b/resources/views/v1/budgets/show.twig index abe743d46a..749a165672 100644 --- a/resources/views/v1/budgets/show.twig +++ b/resources/views/v1/budgets/show.twig @@ -16,8 +16,6 @@ {{ trans('firefly.chart_all_journals_for_budget', {name:budget.name}) }} {% endif %} </h3> - - <div class="box-tools pull-right"> <div class="btn-group"> <button class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown"><i class="fa fa-ellipsis-v"></i></button> diff --git a/resources/views/v1/reports/partials/balance.twig b/resources/views/v1/reports/partials/balance.twig index f3cdf36345..a84015f556 100644 --- a/resources/views/v1/reports/partials/balance.twig +++ b/resources/views/v1/reports/partials/balance.twig @@ -4,7 +4,8 @@ <th>{{ 'budgets'|_ }}</th> {% for account in report.accounts %} {% if account.sum != 0 %} - <th class="hidden-xs" style="text-align: right;"><a href="{{ route('accounts.show',account.id) }}" title="{{ account.iban|default(account.name) }}">{{ account.name }}</a></th> + <th class="hidden-xs" style="text-align: right;"><a href="{{ route('accounts.show',account.id) }}" + title="{{ account.iban|default(account.name) }}">{{ account.name }}</a></th> {% endif %} {% endfor %} <th style="text-align: right;">{{ 'sum'|_ }}</th> @@ -14,31 +15,34 @@ {% for budget in report.budgets %} {% if budget.spent|length > 0 %} - <tr> - <td> - <a href="{{ route('budgets.show', [budget.budget_id]) }}">{{ budget.budget_name }}</a> - </td> - {% for account in report.accounts %} - {% if budget.spent[account.id] %} - <td style="text-align: right;"> - {{ formatAmountBySymbol(budget.spent[account.id].spent, budget.spent[account.id].currency_symbol, budget.spent[account.id].currency_decimal_places) }} - <i class="fa fa-fw fa-info-circle text-muted"></i> - {# TODO #} - {#data-location="category-entry" data-category-id="{{ category.id }}" data-currency-id="{{ category.currency_id }}"-->#} + <tr> + <td> + <a href="{{ route('budgets.show', [budget.budget_id]) }}">{{ budget.budget_name }}</a> </td> - {% else %} - {% if report.accounts[account.id].sum != 0 %} - <td> </td> - {% endif %} - {% endif %} + {% for account in report.accounts %} + {% if budget.spent[account.id] %} + <td style="text-align: right;"> + {{ formatAmountBySymbol(budget.spent[account.id].spent, budget.spent[account.id].currency_symbol, budget.spent[account.id].currency_decimal_places) }} + <i data-location="budget-entry" + data-budget-id="{{ budget.budget_id }}" + data-account-id="{{ account.id }}" + data-currency-id="{{ budget.spent[account.id].currency_id }}" + class="fa fa-fw fa-info-circle text-muted firefly-info-button"></i> + </td> + {% else %} + {% if report.accounts[account.id].sum != 0 %} + <td> </td> + {% endif %} + {% endif %} - {% endfor %} - <td style="text-align: right;"> - {% for sum in report.sums[budget.budget_id] %} - {{ formatAmountBySymbol(sum.sum, sum.currency_symbol, sum.currency_decimal_places) }} <i class="fa fa-fw fa-info-circle text-muted"></i><br /> - {% endfor %} - </td> - </tr> + {% endfor %} + <td style="text-align: right;"> + {% for sum in report.sums[budget.budget_id] %} + {{ formatAmountBySymbol(sum.sum, sum.currency_symbol, sum.currency_decimal_places) }} <i class="fa fa-fw fa-info-circle text-muted"></i> + <br/> + {% endfor %} + </td> + </tr> {% endif %} {% endfor %} </tbody> @@ -47,9 +51,9 @@ <td><em>{{ 'sum'|_ }}</em></td> {% for account in report.accounts %} {% if account.sum != 0 %} - <td style="text-align: right;"> + <td style="text-align: right;"> {{ formatAmountBySymbol(account.sum, account.currency_symbol, account.currency_decimal_places) }} - </td> + </td> {% endif %} {% endfor %} </tr> diff --git a/resources/views/v1/reports/partials/budget-period.twig b/resources/views/v1/reports/partials/budget-period.twig index 11655bebde..db3ee7f67e 100644 --- a/resources/views/v1/reports/partials/budget-period.twig +++ b/resources/views/v1/reports/partials/budget-period.twig @@ -29,7 +29,6 @@ {% else %} <td data-value="0" style="text-align: right;"> {{ formatAmountBySymbol(0, info.currency_symbol, info.currency_decimal_places) }} - </td> {% endif %} diff --git a/resources/views/v1/reports/partials/categories.twig b/resources/views/v1/reports/partials/categories.twig index c2e8c3593e..8be6b25415 100644 --- a/resources/views/v1/reports/partials/categories.twig +++ b/resources/views/v1/reports/partials/categories.twig @@ -22,8 +22,7 @@ <td style="text-align: right;">{{ formatAmountBySymbol(category.earned, category.currency_symbol, category.currency_decimal_places, true) }}</td> <td style="text-align: right;">{{ formatAmountBySymbol(category.sum, category.currency_symbol, category.currency_decimal_places, true) }}</td> <td style="width:20px;"> - <i class="fa fa-fw fa-info-circle text-muted firefly-info-button" - data-location="category-entry" data-category-id="{{ category.id }}" data-currency-id="{{ category.currency_id }}" + <i class="fa fa-fw fa-info-circle text-muted firefly-info-button" data-location="category-entry" data-category-id="{{ category.id }}" data-currency-id="{{ category.currency_id }}" ></i> </td> </tr> diff --git a/routes/web.php b/routes/web.php index a1cd3c884c..3cedcbd4e1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -229,7 +229,7 @@ Route::group( */ Route::group( ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers', 'prefix' => 'available-budgets', 'as' => 'available-budgets.'], - static function () {s + static function () { // create Route::get('create/{start_date}/{end_date}/{currency?}', ['uses' => 'Budget\AvailableBudgetController@create', 'as' => 'create']);