diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index d3166ead4e..87557c8682 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -131,6 +131,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface return $this; } + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface + { + return $this; + } + /** * @param Collection $collection * @param int $sortFlag diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index d137b6e8b5..0a3c60be3a 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -141,6 +141,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface return $this; } + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface + { + return $this; + } + /** * @param Collection $collection * @param int $sortFlag diff --git a/app/Generator/Report/ReportGeneratorInterface.php b/app/Generator/Report/ReportGeneratorInterface.php index 593d318f10..12942190f4 100644 --- a/app/Generator/Report/ReportGeneratorInterface.php +++ b/app/Generator/Report/ReportGeneratorInterface.php @@ -64,4 +64,11 @@ interface ReportGeneratorInterface */ public function setStartDate(Carbon $date): ReportGeneratorInterface; + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface; + } diff --git a/app/Generator/Report/Standard/MonthReportGenerator.php b/app/Generator/Report/Standard/MonthReportGenerator.php index 113b777c91..bf4d1889ca 100644 --- a/app/Generator/Report/Standard/MonthReportGenerator.php +++ b/app/Generator/Report/Standard/MonthReportGenerator.php @@ -106,4 +106,14 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } + + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface + { + return $this; + } } diff --git a/app/Generator/Report/Standard/MultiYearReportGenerator.php b/app/Generator/Report/Standard/MultiYearReportGenerator.php index 5dab371d6b..a5b1702a95 100644 --- a/app/Generator/Report/Standard/MultiYearReportGenerator.php +++ b/app/Generator/Report/Standard/MultiYearReportGenerator.php @@ -103,4 +103,14 @@ class MultiYearReportGenerator implements ReportGeneratorInterface return $this; } + + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface + { + return $this; + } } diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php index f3297e35df..e755b45057 100644 --- a/app/Generator/Report/Standard/YearReportGenerator.php +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -103,4 +103,14 @@ class YearReportGenerator implements ReportGeneratorInterface return $this; } + + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface + { + return $this; + } } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index e2348dc6d8..5c32e086f9 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -19,9 +19,11 @@ use FireflyIII\Generator\Report\ReportGeneratorFactory; use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Requests\ReportFormRequest; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; use Preferences; @@ -96,6 +98,42 @@ class ReportController extends Controller } + /** + * @param Collection $accounts + * @param Collection $tags + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function tagReport(Collection $accounts, Collection $tags, 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_tag', + [ + 'start' => $start->formatLocalized($this->monthFormat), + 'end' => $end->formatLocalized($this->monthFormat), + ] + ) + ); + + $generator = ReportGeneratorFactory::reportGenerator('Tag', $start, $end); + $generator->setAccounts($accounts); + $generator->setTags($tags); + $result = $generator->generate(); + + return $result; + + } + /** * @param Collection $accounts * @param Collection $budgets @@ -238,6 +276,9 @@ class ReportController extends Controller case 'budget': $result = $this->budgetReportOptions(); break; + case 'tag': + $result = $this->tagReportOptions(); + break; } return Response::json(['html' => $result]); @@ -258,6 +299,7 @@ class ReportController extends Controller $accounts = join(',', $request->getAccountList()->pluck('id')->toArray()); $categories = join(',', $request->getCategoryList()->pluck('id')->toArray()); $budgets = join(',', $request->getBudgetList()->pluck('id')->toArray()); + $tags = join(',', $request->getTagList()->pluck('tag')->toArray()); if ($request->getAccountList()->count() === 0) { Session::flash('error', trans('firefly.select_more_than_one_account')); @@ -277,6 +319,12 @@ class ReportController extends Controller return redirect(route('reports.index')); } + if ($request->getTagList()->count() === 0 && $reportType === 'tag') { + Session::flash('error', trans('firefly.select_more_than_one_tag')); + + return redirect(route('reports.index')); + } + if ($end < $start) { return view('error')->with('message', trans('firefly.end_after_start_date')); } @@ -301,6 +349,9 @@ class ReportController extends Controller case 'budget': $uri = route('reports.report.budget', [$accounts, $budgets, $start, $end]); break; + case 'tag': + $uri = route('reports.report.tag', [$accounts, $tags, $start, $end]); + break; } return redirect($uri); @@ -341,4 +392,22 @@ class ReportController extends Controller { return view('reports.options.no-options')->render(); } + + /** + * @return string + */ + private function tagReportOptions(): string + { + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class); + $tags = $repository->get()->sortBy( + function (Tag $tag) { + return $tag->tag; + } + ); + $result = view('reports.options.tag', compact('tags'))->render(); + + return $result; + + } } diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 763af9dd6a..02a5f5c8e5 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -19,6 +19,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; /** @@ -141,13 +142,34 @@ class ReportFormRequest extends Request return $date; } + /** + * @return Collection + */ + public function getTagList(): Collection + { + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class); + $set = $this->get('tag'); + $collection = new Collection; + if (is_array($set)) { + foreach ($set as $tagTag) { + $tag = $repository->findByTag($tagTag); + if (!is_null($tag->id)) { + $collection->push($tag); + } + } + } + + return $collection; + } + /** * @return array */ public function rules(): array { return [ - 'report_type' => 'in:audit,default,category,budget', + 'report_type' => 'in:audit,default,category,budget,tag', ]; } diff --git a/app/Support/Binder/TagList.php b/app/Support/Binder/TagList.php new file mode 100644 index 0000000000..b1c42bb4c8 --- /dev/null +++ b/app/Support/Binder/TagList.php @@ -0,0 +1,52 @@ +check()) { + $tags = explode(',', $value); + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class); + $allTags = $repository->get(); + $set = $allTags->filter( + function (Tag $tag) use ($tags) { + return in_array($tag->tag, $tags); + } + ); + + if ($set->count() > 0) { + return $set; + } + } + throw new NotFoundHttpException; + } +} diff --git a/config/firefly.php b/config/firefly.php index adae9e8c07..5ffebe0c43 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -153,6 +153,7 @@ return [ 'budgetList' => 'FireflyIII\Support\Binder\BudgetList', 'journalList' => 'FireflyIII\Support\Binder\JournalList', 'categoryList' => 'FireflyIII\Support\Binder\CategoryList', + 'tagList' => 'FireflyIII\Support\Binder\TagList', 'start_date' => 'FireflyIII\Support\Binder\Date', 'end_date' => 'FireflyIII\Support\Binder\Date', ], @@ -176,7 +177,7 @@ return [ 'transaction_type' => 'FireflyIII\Rules\Triggers\TransactionType', 'category_is' => 'FireflyIII\Rules\Triggers\CategoryIs', 'budget_is' => 'FireflyIII\Rules\Triggers\BudgetIs', - 'tag_is' => 'FireflyIII\Rules\Triggers\TagIs', + 'tag_is' => 'FireflyIII\Rules\Triggers\TagIs', ], 'rule-actions' => [ 'set_category' => 'FireflyIII\Rules\Actions\SetCategory', diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index b57faf8b04..df65b9704f 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -98,8 +98,8 @@ function setOptionalFromCookies() { arr.forEach(function (val) { $('#inputCategories').find('option[value="' + val + '"]').prop('selected', true); }); - $('#inputCategories').multiselect(defaultMultiSelect); } + $('#inputCategories').multiselect(defaultMultiSelect); // and budgets! if ((readCookie('report-budgets') !== null)) { @@ -107,8 +107,17 @@ function setOptionalFromCookies() { arr.forEach(function (val) { $('#inputBudgets').find('option[value="' + val + '"]').prop('selected', true); }); - $('#inputBudgets').multiselect(defaultMultiSelect); } + $('#inputBudgets').multiselect(defaultMultiSelect); + + // and tags! + if ((readCookie('report-tags') !== null)) { + arr = readCookie('report-tags').split(','); + arr.forEach(function (val) { + $('#inputBudgets').find('option[value="' + val + '"]').prop('selected', true); + }); + } + $('#inputTags').multiselect(defaultMultiSelect); } function catchSubmit() { @@ -117,45 +126,20 @@ function catchSubmit() { var picker = $('#inputDateRange').data('daterangepicker'); // all account ids: - var count = 0; - var accounts = []; - $.each($('.account-checkbox'), function (i, v) { - var c = $(v); - if (c.prop('checked')) { - accounts.push(c.val()); - count++; - } - }); - - // all category ids: - var categories = []; - $.each($('.category-checkbox'), function (i, v) { - var c = $(v); - if (c.prop('checked')) { - categories.push(c.val()); - } - }); - - // all budget ids: - var budgets = []; - $.each($('.budget-checkbox'), function (i, v) { - var c = $(v); - if (c.prop('checked')) { - budgets.push(c.val()); - } - }); - + var accounts = $('#inputAccounts').val(); + var categories = $('#inputCategories').val(); + var budgets = $('#inputBudgets').val(); + var tags = $('#inputTags').val(); // remember all - if (count > 0) { - // set cookie to remember choices. - 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); - } + // set cookie to remember choices. + 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-tags', tags, 365); + createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365); + createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365); return true; } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 5bc5ae351d..23d501fdb0 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -554,6 +554,7 @@ return [ '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', + 'select_more_than_one_tag' => 'Please select more than one tag', 'from_to' => 'From :start to :end', // categories: @@ -711,6 +712,7 @@ return [ 'report_type_audit' => 'Transaction history overview (audit)', 'report_type_category' => 'Category report', 'report_type_budget' => 'Budget report', + 'report_type_tag' => 'Tag 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', @@ -729,8 +731,9 @@ return [ 'report_has_no_extra_options' => 'This report has no extra options', '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.', + 'select_category' => 'Select category(ies)', + 'select_budget' => 'Select budget(s).', + 'select_tag' => 'Select tag(s).', 'income_per_category' => 'Income per category', 'expense_per_category' => 'Expense per category', 'expense_per_budget' => 'Expense per budget', @@ -961,5 +964,5 @@ return [ 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the tag that was created for this import.', // sandstorm.io errors and messages: - 'sandstorm_not_available' => 'This function is not available when you are using Firefly III within a Sandstorm.io environment.', + 'sandstorm_not_available' => 'This function is not available when you are using Firefly III within a Sandstorm.io environment.', ]; diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 85771558c0..d46984b7ea 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -28,6 +28,7 @@ + diff --git a/resources/views/reports/options/budget.twig b/resources/views/reports/options/budget.twig index 85d9bac773..ee85764b89 100644 --- a/resources/views/reports/options/budget.twig +++ b/resources/views/reports/options/budget.twig @@ -1,8 +1,10 @@ -

- {{ 'select_budget'|_ }} -

- +
+ +
+ +
+
\ No newline at end of file diff --git a/resources/views/reports/options/category.twig b/resources/views/reports/options/category.twig index 665e60421e..2ff52d301a 100644 --- a/resources/views/reports/options/category.twig +++ b/resources/views/reports/options/category.twig @@ -1,8 +1,11 @@ -

- {{ 'select_category'|_ }} -

- +
+ +
+ + +
+
diff --git a/resources/views/reports/options/tag.twig b/resources/views/reports/options/tag.twig new file mode 100644 index 0000000000..54d4663a0d --- /dev/null +++ b/resources/views/reports/options/tag.twig @@ -0,0 +1,10 @@ +
+ +
+ +
+
diff --git a/routes/web.php b/routes/web.php index 1210b72f1a..c446a58ff9 100755 --- a/routes/web.php +++ b/routes/web.php @@ -478,6 +478,7 @@ Route::group( 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::get('tag/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'ReportController@tagReport', 'as' => 'report.tag']); Route::post('', ['uses' => 'ReportController@postIndex', 'as' => 'index.post']); }