From edb5b2ed5e4015f542f355b9679b806cbc4e559a Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:50:20 +0100 Subject: [PATCH 001/223] Initial code for new budget report #426 --- .../Report/Audit/MonthReportGenerator.php | 10 +++ .../Report/Category/MonthReportGenerator.php | 11 +++- .../Report/ReportGeneratorInterface.php | 7 ++ .../Report/Standard/MonthReportGenerator.php | 10 +++ .../Standard/MultiYearReportGenerator.php | 10 +++ .../Report/Standard/YearReportGenerator.php | 10 +++ app/Http/Controllers/ReportController.php | 64 +++++++++++++++++++ app/Http/Requests/ReportFormRequest.php | 24 ++++++- public/js/ff/reports/index.js | 23 ++++++- resources/lang/en_US/firefly.php | 2 + resources/views/reports/index.twig | 1 + resources/views/reports/options/budget.twig | 8 +++ routes/web.php | 1 + 13 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 resources/views/reports/options/budget.twig diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 7caff56bb4..f5ab29a5bb 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -110,6 +110,16 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + return $this; + } + /** * @param Collection $categories * diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index a72eb65f68..cbac1ee2d9 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -15,7 +15,6 @@ namespace FireflyIII\Generator\Report\Category; use Carbon\Carbon; -use Crypt; use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\Transaction; @@ -95,6 +94,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface return $this; } + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + return $this; + } + /** * @param Collection $categories * diff --git a/app/Generator/Report/ReportGeneratorInterface.php b/app/Generator/Report/ReportGeneratorInterface.php index e14ed4fb91..ad46c0646a 100644 --- a/app/Generator/Report/ReportGeneratorInterface.php +++ b/app/Generator/Report/ReportGeneratorInterface.php @@ -36,6 +36,13 @@ interface ReportGeneratorInterface */ public function setAccounts(Collection $accounts): ReportGeneratorInterface; + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface; + /** * @param Collection $categories * diff --git a/app/Generator/Report/Standard/MonthReportGenerator.php b/app/Generator/Report/Standard/MonthReportGenerator.php index bcffeedff2..5905e12815 100644 --- a/app/Generator/Report/Standard/MonthReportGenerator.php +++ b/app/Generator/Report/Standard/MonthReportGenerator.php @@ -63,6 +63,16 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + return $this; + } + /** * @param Collection $categories * diff --git a/app/Generator/Report/Standard/MultiYearReportGenerator.php b/app/Generator/Report/Standard/MultiYearReportGenerator.php index 64f74f1295..3437283426 100644 --- a/app/Generator/Report/Standard/MultiYearReportGenerator.php +++ b/app/Generator/Report/Standard/MultiYearReportGenerator.php @@ -60,6 +60,16 @@ class MultiYearReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + return $this; + } + /** * @param Collection $categories * diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php index 7536b4027f..545084909f 100644 --- a/app/Generator/Report/Standard/YearReportGenerator.php +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -60,6 +60,16 @@ class YearReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + return $this; + } + /** * @param Collection $categories * diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 655c3b2a22..19468b77b1 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -20,6 +20,7 @@ use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Requests\ReportFormRequest; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; @@ -95,6 +96,42 @@ class ReportController extends Controller } + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function budgetReport(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) + { + if ($end < $start) { + return view('error')->with('message', trans('firefly.end_after_start_date')); + } + if ($start < session('first')) { + $start = session('first'); + } + + View::share( + 'subTitle', trans( + 'firefly.report_budget', + [ + 'start' => $start->formatLocalized($this->monthFormat), + 'end' => $end->formatLocalized($this->monthFormat), + ] + ) + ); + + $generator = ReportGeneratorFactory::reportGenerator('Budget', $start, $end); + $generator->setAccounts($accounts); + $generator->setBudgets($budgets); + $result = $generator->generate(); + + return $result; + + } + /** * @param Collection $accounts * @param Collection $categories @@ -198,6 +235,9 @@ class ReportController extends Controller case 'category': $result = $this->categoryReportOptions(); break; + case 'budget': + $result = $this->budgetReportOptions(); + break; } return Response::json(['html' => $result]); @@ -217,6 +257,7 @@ class ReportController extends Controller $end = $request->getEndDate()->format('Ymd'); $accounts = join(',', $request->getAccountList()->pluck('id')->toArray()); $categories = join(',', $request->getCategoryList()->pluck('id')->toArray()); + $budgets = join(',', $request->getBudgetList()->pluck('id')->toArray()); if ($request->getAccountList()->count() === 0) { Session::flash('error', trans('firefly.select_more_than_one_account')); @@ -230,6 +271,12 @@ class ReportController extends Controller return redirect(route('reports.index')); } + if ($request->getBudgetList()->count() === 0 && $reportType === 'budget') { + Session::flash('error', trans('firefly.select_more_than_one_budget')); + + return redirect(route('reports.index')); + } + if ($end < $start) { return view('error')->with('message', trans('firefly.end_after_start_date')); } @@ -251,11 +298,28 @@ class ReportController extends Controller case 'audit': $uri = route('reports.report.audit', [$accounts, $start, $end]); break; + case 'budget': + $uri = route('reports.report.budget', [$accounts, $budgets, $start, $end]); + break; } return redirect($uri); } + /** + * @return string + */ + private function budgetReportOptions(): string + { + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + $budgets = $repository->getBudgets(); + $result = view('reports.options.budget', compact('budgets'))->render(); + + return $result; + + } + /** * @return string */ diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 7d97a37b76..fbb153bcb9 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -17,6 +17,7 @@ use Carbon\Carbon; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use Illuminate\Support\Collection; @@ -79,6 +80,27 @@ class ReportFormRequest extends Request return $collection; } + /** + * @return Collection + */ + public function getBudgetList(): Collection + { + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + $set = $this->get('budget'); + $collection = new Collection; + if (is_array($set)) { + foreach ($set as $budgetId) { + $budget = $repository->find(intval($budgetId)); + if (!is_null($budget->id)) { + $collection->push($budget); + } + } + } + + return $collection; + } + /** * @return Carbon * @throws FireflyException @@ -125,7 +147,7 @@ class ReportFormRequest extends Request public function rules(): array { return [ - 'report_type' => 'in:audit,default,category', + 'report_type' => 'in:audit,default,category,budget', ]; } diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index bd949eeac6..9de3f79b50 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -65,12 +65,22 @@ function getReportOptions() { } function setOptionalFromCookies() { + var arr; + // categories if ((readCookie('report-categories') !== null)) { - var arr = readCookie('report-categories').split(','); + arr = readCookie('report-categories').split(','); arr.forEach(function (val) { $('input[class="category-checkbox"][type="checkbox"][value="' + val + '"]').prop('checked', true); }); } + + // and budgets! + if ((readCookie('report-budgets') !== null)) { + arr = readCookie('report-budgets').split(','); + arr.forEach(function (val) { + $('input[class="budget-checkbox"][type="checkbox"][value="' + val + '"]').prop('checked', true); + }); + } } function catchSubmit() { @@ -90,7 +100,6 @@ function catchSubmit() { }); // all category ids: - //category-checkbox var categories = []; $.each($('.category-checkbox'), function (i, v) { var c = $(v); @@ -99,6 +108,15 @@ function catchSubmit() { } }); + // all budget ids: + var budgets = []; + $.each($('.budget-checkbox'), function (i, v) { + var c = $(v); + if (c.prop('checked')) { + budgets.push(c.val()); + } + }); + // remember all if (count > 0) { @@ -106,6 +124,7 @@ function catchSubmit() { createCookie('report-type', $('select[name="report_type"]').val(), 365); createCookie('report-accounts', accounts, 365); createCookie('report-categories', categories, 365); + createCookie('report-budgets', budgets, 365); createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365); createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365); } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 11d55a5cac..7372a2876a 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Default financial report', 'report_type_audit' => 'Transaction history overview (audit)', 'report_type_category' => 'Category report', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => 'Categories, budgets and bills overview', 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', 'report_included_accounts' => 'Included accounts', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'View report', 'end_after_start_date' => 'End date of report must be after start date.', 'select_category' => 'Select one or more categories.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', 'income_per_account' => 'Income per account', diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index c889d0eee2..70d8fd86db 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -26,6 +26,7 @@ diff --git a/resources/views/reports/options/budget.twig b/resources/views/reports/options/budget.twig new file mode 100644 index 0000000000..b2e0e7862e --- /dev/null +++ b/resources/views/reports/options/budget.twig @@ -0,0 +1,8 @@ +

+ {{ 'select_budget'|_ }} +

+{% for budget in budgets %} + +{% endfor %} diff --git a/routes/web.php b/routes/web.php index 3d9221d2a7..04cacdad0c 100755 --- a/routes/web.php +++ b/routes/web.php @@ -456,6 +456,7 @@ Route::group( Route::get('default/{accountList}/{start_date}/{end_date}', ['uses' => 'ReportController@defaultReport', 'as' => 'report.default']); Route::get('audit/{accountList}/{start_date}/{end_date}', ['uses' => 'ReportController@auditReport', 'as' => 'report.audit']); Route::get('category/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'ReportController@categoryReport', 'as' => 'report.category']); + Route::get('budget/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'ReportController@budgetReport', 'as' => 'report.budget']); Route::post('', ['uses' => 'ReportController@postIndex', 'as' => 'index.post']); } From 0393fcd7049c5ec674dcdf82c66a0597624deeed Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:51:30 +0100 Subject: [PATCH 002/223] Approved. Step name: Proofread --- resources/lang/nl_NL/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 133343784a..c8029e58b8 100644 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Standard financieel rapport', 'report_type_audit' => 'Transactiehistorie-overzicht (audit)', 'report_type_category' => 'Categorierapport', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => 'Overzicht van categorieën, budgetten en contracten', 'more_info_help' => 'Meer informatie over deze rapporten vind je in de hulppagina\'s. Klik daarvoor op het (?) icoontje rechtsboven.', 'report_included_accounts' => 'Accounts in rapport', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'Bekijk overzicht', 'end_after_start_date' => 'Einddatum moet na begindatum liggen.', 'select_category' => 'Selecteer minstens één categorie.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Inkomen per categorie', 'expense_per_category' => 'Uitgaven per categorie', 'income_per_account' => 'Inkomen per rekening', From 9d084e62f7d4d9ae5a52fe6a52a7c852e2e1843b Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:51:53 +0100 Subject: [PATCH 003/223] New translations --- resources/lang/pt_BR/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php index 5368d3515d..48e430e551 100644 --- a/resources/lang/pt_BR/firefly.php +++ b/resources/lang/pt_BR/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Relatório financeiro padrão', 'report_type_audit' => 'Visão geral do histórico de transação (auditoria)', 'report_type_category' => 'Relatório por Categorias', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => 'Visão geral de categorias, orçamentos e faturas', 'more_info_help' => 'Mais informações sobre esses tipos de relatórios podem ser encontradas nas páginas de ajuda. Pressione o ícone (?) no canto superior direito.', 'report_included_accounts' => 'Contas incluídas', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'Visualizar relatório', 'end_after_start_date' => 'Data de término do relatório deve ser depois da data de início.', 'select_category' => 'Selecione uma ou mais categorias.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Receitas por categoria', 'expense_per_category' => 'Despesa por categoria', 'income_per_account' => 'Rendimento por conta', From a4d14f8259be4e1f3c28c83061a5e1094e8a058d Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:52:01 +0100 Subject: [PATCH 004/223] New translations --- resources/lang/zh_HK/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/zh_HK/firefly.php b/resources/lang/zh_HK/firefly.php index 377f4c2ca3..fffc960684 100644 --- a/resources/lang/zh_HK/firefly.php +++ b/resources/lang/zh_HK/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Default financial report', 'report_type_audit' => 'Transaction history overview (audit)', 'report_type_category' => 'Category report', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => 'Categories, budgets and bills overview', 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', 'report_included_accounts' => 'Included accounts', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'View report', 'end_after_start_date' => 'End date of report must be after start date.', 'select_category' => 'Select one or more categories.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', 'income_per_account' => 'Income per account', From 18a845ac5530a0d96e6af2705f4db3263ae545f2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:52:06 +0100 Subject: [PATCH 005/223] New translations --- resources/lang/zh_TW/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/zh_TW/firefly.php b/resources/lang/zh_TW/firefly.php index 9ed680fc3c..19d705877a 100644 --- a/resources/lang/zh_TW/firefly.php +++ b/resources/lang/zh_TW/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Default financial report', 'report_type_audit' => 'Transaction history overview (audit)', 'report_type_category' => 'Category report', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => '類別、 預算與賬單的概覽', 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', 'report_included_accounts' => 'Included accounts', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'View report', 'end_after_start_date' => 'End date of report must be after start date.', 'select_category' => 'Select one or more categories.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', 'income_per_account' => 'Income per account', From cabe2579fa2073abaf5bad5a0728fdbbe8552db1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:52:16 +0100 Subject: [PATCH 006/223] New translations --- resources/lang/fr_FR/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php index cc80087ff2..25d84256b1 100644 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Rapport financier par défaut', 'report_type_audit' => 'Historique des transactions', 'report_type_category' => 'Category report', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => 'Vue d’ensemble des budgets, des catégories et des factures', 'more_info_help' => 'Plus d’informations sur ces types de rapports se trouvent dans les pages d’aide. Appuyez sur l’icône ( ?) dans le coin supérieur droit.', 'report_included_accounts' => 'Comptes inclus', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'View report', 'end_after_start_date' => 'End date of report must be after start date.', 'select_category' => 'Select one or more categories.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', 'income_per_account' => 'Income per account', From b2da38d40177c5f744cc2674151fbad1f45870d2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:52:22 +0100 Subject: [PATCH 007/223] New translations --- resources/lang/de_DE/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/de_DE/firefly.php b/resources/lang/de_DE/firefly.php index 996258c4fe..a18cd68ea6 100644 --- a/resources/lang/de_DE/firefly.php +++ b/resources/lang/de_DE/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Default financial report', 'report_type_audit' => 'Transaction history overview (audit)', 'report_type_category' => 'Category report', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => 'Übersicht über Kategorien, Budgets und Rechnungen', 'more_info_help' => 'Weitere Informationen über diese Art von Berichten finden Sie in der Hilfe. Drücken Sie hierfür das (?)-Symbol in der oberen rechten Ecke.', 'report_included_accounts' => 'Eingezogene Konten', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'Zeige Bericht', 'end_after_start_date' => 'End date of report must be after start date.', 'select_category' => 'Select one or more categories.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', 'income_per_account' => 'Income per account', From b0e120abee2c539f0480ea1f5d461a10ffe49a4d Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 8 Dec 2016 21:52:28 +0100 Subject: [PATCH 008/223] New translations --- resources/lang/hr_HR/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/hr_HR/firefly.php b/resources/lang/hr_HR/firefly.php index 377f4c2ca3..fffc960684 100644 --- a/resources/lang/hr_HR/firefly.php +++ b/resources/lang/hr_HR/firefly.php @@ -679,6 +679,7 @@ return [ 'report_type_default' => 'Default financial report', 'report_type_audit' => 'Transaction history overview (audit)', 'report_type_category' => 'Category report', + 'report_type_budget' => 'Budget report', 'report_type_meta-history' => 'Categories, budgets and bills overview', 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', 'report_included_accounts' => 'Included accounts', @@ -698,6 +699,7 @@ return [ 'reports_submit' => 'View report', 'end_after_start_date' => 'End date of report must be after start date.', 'select_category' => 'Select one or more categories.', + 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', 'income_per_account' => 'Income per account', From c73866f47c15984f055e4585ee14e70a133c472b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 9 Dec 2016 06:28:51 +0100 Subject: [PATCH 009/223] Fixed date [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4db7ca588f..e688d23b82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [4.2.1] - 2015-05-25 +## [4.2.1] - 2016-12-09 ### Added - BIC support (see #430) - New category report section and chart (see the general financial report) From 1fd028dfb8b50b6bfa05091349b9e57941cfb24e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 9 Dec 2016 07:07:53 +0100 Subject: [PATCH 010/223] First code for #426 --- .../Report/Audit/MonthReportGenerator.php | 2 +- .../Report/Budget/MonthReportGenerator.php | 329 ++++++++++++++++++ .../Budget/MultiYearReportGenerator.php | 27 ++ .../Report/Budget/YearReportGenerator.php | 28 ++ app/Http/Controllers/ReportController.php | 2 +- resources/views/reports/budget/month.twig | 262 ++++++++++++++ 6 files changed, 648 insertions(+), 2 deletions(-) create mode 100644 app/Generator/Report/Budget/MonthReportGenerator.php create mode 100644 app/Generator/Report/Budget/MultiYearReportGenerator.php create mode 100644 app/Generator/Report/Budget/YearReportGenerator.php create mode 100644 resources/views/reports/budget/month.twig diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index f5ab29a5bb..cc31219d9e 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -25,7 +25,7 @@ use Steam; /** * Class MonthReportGenerator * - * @package FireflyIII\Generator\Report\Standard + * @package FireflyIII\Generator\Report\Audit */ class MonthReportGenerator implements ReportGeneratorInterface { diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php new file mode 100644 index 0000000000..10b5c39afe --- /dev/null +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -0,0 +1,329 @@ +income = new Collection; + $this->expenses = new Collection; + } + + /** + * @return string + */ + public function generate(): string + { + $accountIds = join(',', $this->accounts->pluck('id')->toArray()); + $categoryIds = join(',', $this->budgets->pluck('id')->toArray()); + $reportType = 'budget'; + // $expenses = $this->getExpenses(); + // $income = $this->getIncome(); + // $accountSummary = $this->getObjectSummary($this->summarizeByAccount($expenses), $this->summarizeByAccount($income)); + // $categorySummary = $this->getObjectSummary($this->summarizeByCategory($expenses), $this->summarizeByCategory($income)); + // $averageExpenses = $this->getAverages($expenses, SORT_ASC); + // $averageIncome = $this->getAverages($income, SORT_DESC); + // $topExpenses = $this->getTopExpenses(); + // $topIncome = $this->getTopIncome(); + + + // render! + return view( + 'reports.budget.month', + compact( + 'accountIds', 'categoryIds', 'topIncome', 'reportType', 'accountSummary', 'categorySummary', 'averageExpenses', 'averageIncome', 'topExpenses' + ) + ) + ->with('start', $this->start)->with('end', $this->end) + ->with('budgets', $this->budgets) + ->with('accounts', $this->accounts) + ->render(); + } + + /** + * @param Collection $accounts + * + * @return ReportGeneratorInterface + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + return $this; + } + + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + $this->budgets = $budgets; + + return $this; + } + + /** + * @param Collection $categories + * + * @return ReportGeneratorInterface + */ + public function setCategories(Collection $categories): ReportGeneratorInterface + { + $this->categories = $categories; + + return $this; + } + + /** + * @param Carbon $date + * + * @return ReportGeneratorInterface + */ + public function setEndDate(Carbon $date): ReportGeneratorInterface + { + $this->end = $date; + + return $this; + } + + /** + * @param Carbon $date + * + * @return ReportGeneratorInterface + */ + public function setStartDate(Carbon $date): ReportGeneratorInterface + { + $this->start = $date; + + return $this; + } + + /** + * @param Collection $collection + * @param int $sortFlag + * + * @return array + */ + private function getAverages(Collection $collection, int $sortFlag): array + { + $result = []; + /** @var Transaction $transaction */ + foreach ($collection as $transaction) { + // opposing name and ID: + $opposingId = $transaction->opposing_account_id; + + // is not set? + if (!isset($result[$opposingId])) { + $name = $transaction->opposing_account_name; + $result[$opposingId] = [ + 'name' => $name, + 'count' => 1, + 'id' => $opposingId, + 'average' => $transaction->transaction_amount, + 'sum' => $transaction->transaction_amount, + ]; + continue; + } + $result[$opposingId]['count']++; + $result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount); + $result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count'])); + } + + // sort result by average: + $average = []; + foreach ($result as $key => $row) { + $average[$key] = floatval($row['average']); + } + + array_multisort($average, $sortFlag, $result); + + return $result; + } + + /** + * @return Collection + */ + private function getExpenses(): Collection + { + if ($this->expenses->count() > 0) { + Log::debug('Return previous set of expenses.'); + + return $this->expenses; + } + + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setCategories($this->categories)->withOpposingAccount()->disableFilter(); + + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $transactions = self::filterExpenses($transactions, $accountIds); + $this->expenses = $transactions; + + return $transactions; + } + + /** + * @return Collection + */ + private function getIncome(): Collection + { + if ($this->income->count() > 0) { + return $this->income; + } + + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) + ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->setCategories($this->categories)->withOpposingAccount(); + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $transactions = self::filterIncome($transactions, $accountIds); + $this->income = $transactions; + + return $transactions; + } + + /** + * @param array $spent + * @param array $earned + * + * @return array + */ + private function getObjectSummary(array $spent, array $earned): array + { + $return = []; + + /** + * @var int $accountId + * @var string $entry + */ + foreach ($spent as $objectId => $entry) { + if (!isset($return[$objectId])) { + $return[$objectId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$objectId]['spent'] = $entry; + } + unset($entry); + + /** + * @var int $accountId + * @var string $entry + */ + foreach ($earned as $objectId => $entry) { + if (!isset($return[$objectId])) { + $return[$objectId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$objectId]['earned'] = $entry; + } + + + return $return; + } + + + /** + * @return Collection + */ + private function getTopExpenses(): Collection + { + $transactions = $this->getExpenses()->sortBy('transaction_amount'); + + return $transactions; + } + + /** + * @return Collection + */ + private function getTopIncome(): Collection + { + $transactions = $this->getIncome()->sortByDesc('transaction_amount'); + + return $transactions; + } + + /** + * @param Collection $collection + * + * @return array + */ + private function summarizeByAccount(Collection $collection): array + { + $result = []; + /** @var Transaction $transaction */ + foreach ($collection as $transaction) { + $accountId = $transaction->account_id; + $result[$accountId] = $result[$accountId] ?? '0'; + $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); + } + + return $result; + } + + /** + * @param Collection $collection + * + * @return array + */ + private function summarizeByCategory(Collection $collection): array + { + $result = []; + /** @var Transaction $transaction */ + foreach ($collection as $transaction) { + $jrnlCatId = intval($transaction->transaction_journal_category_id); + $transCatId = intval($transaction->transaction_category_id); + $categoryId = max($jrnlCatId, $transCatId); + $result[$categoryId] = $result[$categoryId] ?? '0'; + $result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]); + } + + return $result; + } +} \ No newline at end of file diff --git a/app/Generator/Report/Budget/MultiYearReportGenerator.php b/app/Generator/Report/Budget/MultiYearReportGenerator.php new file mode 100644 index 0000000000..3d92def9f3 --- /dev/null +++ b/app/Generator/Report/Budget/MultiYearReportGenerator.php @@ -0,0 +1,27 @@ + +
+
+
+

{{ 'accounts'|_ }}

+
+
+ + + + + + + + + + {% for account in accounts %} + + + {% if accountSummary[account.id] %} + + {% else %} + + {% endif %} + {% if accountSummary[account.id] %} + + {% else %} + + {% endif %} + + {% endfor %} + +
{{ 'name'|_ }}{{ 'earned'|_ }}{{ 'spent'|_ }}
+ {{ account.name }} + {{ accountSummary[account.id].earned|formatAmount }}{{ 0|formatAmount }}{{ accountSummary[account.id].spent|formatAmount }}{{ 0|formatAmount }}
+
+
+ +
+
+

{{ 'budgets'|_ }}

+
+
+ + + + + + + + + {% for budget in budgets %} + + + {% if budgetSummary[budget.id] %} + + {% else %} + + {% endif %} + {% if budgetSummary[budget.id] %} + + {% else %} + + {% endif %} + + {% endfor %} + +
{{ 'name'|_ }}{{ 'spent'|_ }}
+ {{ budget.name }} + {{ budgetSummary[budget.id].earned|formatAmount }}{{ 0|formatAmount }}{{ budgetSummary[budget.id].spent|formatAmount }}{{ 0|formatAmount }}
+
+
+
+ {% if budgets.count > 1 %} +
+
+
+

{{ 'expense_per_budget'|_ }}

+
+
+ + +
+
+
+ {% endif %} + {% if accounts.count > 1 %} +
+
+
+

{{ 'expense_per_budget'|_ }}

+
+
+ + +
+
+
+ {% endif %} + + +
+
+
+
+

{{ 'income_and_expenses'|_ }}

+
+
+ +
+
+
+
+
+ {% if averageExpenses|length > 0 %} +
+
+
+

{{ 'average_spending_per_account'|_ }}

+
+
+ + + + + + + + + + + {% for row in averageExpenses %} + + + + + + + {% endfor %} + +
{{ 'account'|_ }}{{ 'spent_average'|_ }}{{ 'total'|_ }}{{ 'transaction_count'|_ }}
+ {{ row.name }} + + {{ row.average|formatAmount }} + + {{ row.sum|formatAmount }} + + {{ row.count }} +
+
+
+
+ {% endif %} + {% if topExpenses.count > 0 %} +
+ +
+
+

{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})

+
+
+ + + + + + + + + + + {% for row in topExpenses %} + {% if loop.index > listLength %} + + {% else %} + + {% endif %} + + + + + + {% endfor %} + + + {% if topExpenses.count > listLength %} + + + + {% endif %} + +
{{ 'description'|_ }}{{ 'date'|_ }}{{ 'account'|_ }}{{ 'amount'|_ }}
+ + {% if row.transaction_description|length > 0 %} + {{ row.transaction_description }} ({{ row.description }}) + {% else %} + {{ row.description }} + {% endif %} + + + {{ row.date.formatLocalized(monthAndDayFormat) }} + + + {{ row.opposing_account_name }} + + + {{ row.transaction_amount|formatAmount }} +
+ {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} +
+
+
+
+ {% endif %} +
+ +{% endblock %} + +{% block scripts %} + + + + + + + + + + + +{% endblock %} + +{% block styles %} + +{% endblock %} \ No newline at end of file From 97e6afe3dcd2b43b9d0fe8ec12916b6c6b907625 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 9 Dec 2016 07:08:09 +0100 Subject: [PATCH 011/223] New text to be translated. --- resources/lang/en_US/firefly.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 7372a2876a..0ff5b24d81 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -525,6 +525,7 @@ return [ 'no_data_for_chart' => 'There is not enough information (yet) to generate this chart.', 'select_more_than_one_account' => 'Please select more than one account', 'select_more_than_one_category' => 'Please select more than one category', + 'select_more_than_one_budget' => 'Please select more than one budget', // categories: 'new_category' => 'New category', @@ -633,6 +634,7 @@ return [ 'report_default' => 'Default financial report for :start until :end', 'report_audit' => 'Transaction history overview for :start until :end', 'report_category' => 'Category report for :start until :end', + 'report_budget' => 'Budget report for :start until :end', 'quick_link_reports' => 'Quick links', 'quick_link_default_report' => 'Default financial report', 'quick_link_audit_report' => 'Transaction history overview', @@ -702,9 +704,11 @@ return [ 'select_budget' => 'Select one or more budgets.', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', + 'expense_per_budget' => 'Expense per budget', 'income_per_account' => 'Income per account', 'expense_per_account' => 'Expense per account', - 'include_not_in_category' => 'Include transactions not selected for this report', + 'include_not_in_category' => 'Include categories not selected for this report', + 'include_not_in_budget' => 'Include budgets not selected for this report', 'everything_else' => 'Everything else', 'income_and_expenses' => 'Income and expenses', 'spent_average' => 'Spent (average)', @@ -827,6 +831,7 @@ return [ 'setting_single_user_mode_explain' => 'By default, Firefly III only accepts one (1) registration: you. This is a security measure, preventing others from using your instance unless you allow them to. Future registrations are blocked. When you uncheck this box, others can use your instance as wel, assuming they can reach it (when it is connected to the internet).', 'store_configuration' => 'Store configuration', 'single_user_administration' => 'User administration for :email', + 'edit_user' => 'Edit user :email', 'hidden_fields_preferences' => 'Not all fields are visible right now. You must enable them in your settings.', 'user_data_information' => 'User data', 'user_information' => 'User information', From 052957bbd0fb699a98ccf05091bf0e93cf510aed Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 9 Dec 2016 07:08:20 +0100 Subject: [PATCH 012/223] New view for edit user --- app/Http/Controllers/Admin/UserController.php | 30 +++++++++++++++---- resources/views/admin/users/edit.twig | 14 +++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 resources/views/admin/users/edit.twig diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index ac1f2866f0..cd41a758ba 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -19,6 +19,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Preferences; +use View; /** * Class UserController @@ -27,28 +28,45 @@ use Preferences; */ class UserController extends Controller { + /** + * + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + View::share('title', strval(trans('firefly.administration'))); + View::share('mainTitleIcon', 'fa-hand-spock-o'); + + return $next($request); + } + ); + } + /** * @param User $user * - * @return int + * @return View */ public function edit(User $user) { - return $user->id; + $subTitle = strval(trans('firefly.edit_user', ['email' => $user->email])); + $subTitleIcon = 'fa-user-o'; + + return view('admin.users.edit', compact('user', 'subTitle', 'subTitleIcon')); } /** * @param UserRepositoryInterface $repository * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @return View */ public function index(UserRepositoryInterface $repository) { - $title = strval(trans('firefly.administration')); - $mainTitleIcon = 'fa-hand-spock-o'; $subTitle = strval(trans('firefly.user_administration')); $subTitleIcon = 'fa-users'; $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; @@ -77,7 +95,7 @@ class UserController extends Controller ); - return view('admin.users.index', compact('title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'users')); + return view('admin.users.index', compact('subTitle', 'subTitleIcon', 'users')); } diff --git a/resources/views/admin/users/edit.twig b/resources/views/admin/users/edit.twig new file mode 100644 index 0000000000..00536742a0 --- /dev/null +++ b/resources/views/admin/users/edit.twig @@ -0,0 +1,14 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, user) }} +{% endblock %} +{% block content %} +
+
+

+ This page is empty. +

+
+
+{% endblock %} From ac92939429e296582fe8a030148cb93c96ef471e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 9 Dec 2016 07:08:31 +0100 Subject: [PATCH 013/223] Test to see if bread crumb present. --- .../acceptance/Controllers/AccountControllerTest.php | 12 ++++++++++++ .../Admin/ConfigurationControllerTest.php | 3 +++ .../Controllers/Admin/DomainControllerTest.php | 2 ++ .../Controllers/Admin/HomeControllerTest.php | 2 ++ .../Controllers/Admin/UserControllerTest.php | 6 ++++++ 5 files changed, 25 insertions(+) diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php index adf72e3645..f2ec4ffa53 100644 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -34,6 +34,8 @@ class AccountControllerTest extends TestCase $this->be($this->user()); $this->call('GET', route('accounts.create', ['asset'])); $this->assertResponseStatus(200); + // has bread crumb + $this->see('